spree_payu 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 389c665add62d8f5872a7625290a2d83761e1f74cb09319c32649d8030046b5c
4
+ data.tar.gz: b970ec1bf56d496a749d144338b20ea637c2bcec090f1856b931b96ad7d660ac
5
+ SHA512:
6
+ metadata.gz: 1db75bafccfc395cd1d3fe074d12e647c6e1e4a351faef0ad7464e507f62b8b7448416108c6c94fe9bd87e88c8d1fa6b6a824b5bf43f2f214f1ac400c02e9f2c
7
+ data.tar.gz: ef289aad163fc5086c23e33030b812c2da58e04e0f837a48225d0aface75104dea3a8f3f565ebbae763edf525e9c4afba94026d28e2a0c6382d6720c5d89c551
data/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ The Clear BSD License
2
+
3
+ Copyright (c) 2024 Cloud Sailor AS
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted (subject to the limitations in the disclaimer
8
+ below) provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright notice,
11
+ this list of conditions and the following disclaimer.
12
+
13
+ * Redistributions in binary form must reproduce the above copyright
14
+ notice, this list of conditions and the following disclaimer in the
15
+ documentation and/or other materials provided with the distribution.
16
+
17
+ * Neither the name of the copyright holder nor the names of its
18
+ contributors may be used to endorse or promote products derived from this
19
+ software without specific prior written permission.
20
+
21
+ NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22
+ THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ Spree PayU
2
+ ====================
3
+
4
+ PayU payment system for Spree (>= 4.5)
5
+
6
+ Install
7
+ =======
8
+
9
+ Add to your Gemfile:
10
+
11
+ gem 'spree_payu', github: 'cloudsailor/spree_payu', branch: 'master'
12
+
13
+ and run
14
+
15
+ bundle install
16
+
17
+ PayU.pl Settings
18
+ ========
19
+
20
+ You'll have to set the following parameters:
21
+ * payu_client_id
22
+ * payu_pos_id
23
+ * payu_second_key
24
+ * payu_client_secret
25
+ * return_url
26
+ * return_status_url
27
+
28
+ In Spree Admin zone you have to create new payment method and select *Spree::Gateway::Payu* as a provider.
29
+ I recommend to test it first - just select *test mode* in payment method settings and it will use sandbox platform instead of the production one.
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ class Gateway::PayuController < Spree::BaseController
3
+ skip_before_action :verify_authenticity_token, only: :comeback
4
+ include Spree::Core::ControllerHelpers::Order
5
+
6
+ def comeback
7
+ payment = Spree::Payment.find_by("public_metadata->>'token' = ?", params[:order][:orderId])
8
+
9
+ if payment&.state == 'checkout' &&
10
+ payment.payment_method.verify_transaction(params[:order][:status],
11
+ payment,
12
+ params[:order][:totalAmount],
13
+ params[:order][:currencyCode],
14
+ params[:order][:orderId]) &&
15
+ payment.order.update_with_updater!
16
+ head :ok
17
+ else
18
+ head :unprocessable_entity
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,194 @@
1
+ require 'faraday'
2
+ require 'bigdecimal'
3
+
4
+ module Spree
5
+ class Gateway::Payu < PaymentMethod
6
+ preference :payu_client_id, :string
7
+ preference :payu_pos_id, :string
8
+ preference :payu_second_key, :string
9
+ preference :payu_client_secret, :string
10
+ preference :test_mode, :boolean, default: false
11
+ preference :return_url, :string, default: "http://localhost:3000"
12
+ preference :return_status_url, :string, default: "http://localhost:3000"
13
+
14
+ def payment_profiles_supported?
15
+ false
16
+ end
17
+
18
+ def source_required?
19
+ false
20
+ end
21
+
22
+ def cancel(order_id)
23
+ Rails.logger.debug("Starting cancellation for #{order_id}")
24
+
25
+ Rails.logger.debug("Spree order #{order_id} has been canceled.")
26
+ ActiveMerchant::Billing::Response.new(true, 'Spree order has been canceled.')
27
+ end
28
+
29
+ def amount(amount)
30
+ (BigDecimal(amount.to_s) * BigDecimal('100')).to_i.to_s
31
+ end
32
+
33
+ def credit(credit_cents, payment_id, options)
34
+ order = options[:originator].try(:payment).try(:order)
35
+ payment = options[:originator].try(:payment)
36
+ reimbursement = options[:originator].try(:reimbursement)
37
+ order_number = order.try(:number)
38
+ order_currency = order.try(:currency)
39
+
40
+ ActiveMerchant::Billing::Response.new(true, 'Refund successful')
41
+ end
42
+
43
+ def order_url
44
+ if preferred_test_mode
45
+ 'https://secure.snd.payu.com/api/v2_1/orders'
46
+ else
47
+ 'https://secure.payu.com/api/v2_1/orders'
48
+ end
49
+ end
50
+
51
+ def register_order(order, payment_id, gateway_id)
52
+ return if order.blank? || payment_id.blank? || gateway_id.blank?
53
+
54
+ payment = order.payments.find payment_id
55
+
56
+ conn = Faraday.new(url: order_url) do |faraday|
57
+ faraday.adapter Faraday.default_adapter
58
+ faraday.request :authorization, 'Bearer', authorize
59
+ end
60
+
61
+ response = conn.post do |req|
62
+ req.headers['Content-Type'] = 'application/json'
63
+ req.body = register_order_payload(order, payment, gateway_id).to_json
64
+ end
65
+
66
+ if (200..303).include? response.status
67
+ response_body = JSON.parse(response.body)
68
+ payment.update(public_metadata: { token: response_body['orderId'], payment_url: (response_body['redirectUri']+ "&lang=#{order.billing_address.country.iso.downcase}") })
69
+ else
70
+ Rails.logger.warn("register_order #{order.id}, payment_id: #{payment_id} failed => #{response.inspect}")
71
+ nil
72
+ end
73
+ end
74
+
75
+ def register_order_payload(order, payment, gateway_id)
76
+ ip_address = order.last_ip_address || Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
77
+ {
78
+ customerIp: ip_address,
79
+ merchantPosId: preferred_payu_pos_id,
80
+ description: order.store.name,
81
+ currencyCode: order.currency,
82
+ totalAmount: amount(order.total),
83
+ products: items_payload(order.line_items),
84
+ continueUrl: preferred_return_url,
85
+ notifyUrl: "#{preferred_return_status_url}/gateway/payu/comeback/#{gateway_id}/#{order.id}",
86
+ extOrderId: "#{order.number}|#{payment.number}",
87
+ buyer: {
88
+ email: order.email,
89
+ phone: order.billing_address.phone,
90
+ firstName: order.billing_address.firstname,
91
+ lastName: order.billing_address.lastname,
92
+ language: order.billing_address.country.iso.downcase,
93
+ delivery: {
94
+ street: order.shipping_address.address1,
95
+ postalCode: order.shipping_address.zipcode,
96
+ city: order.shipping_address.city,
97
+ countryCode: order.shipping_address.country.iso
98
+ }
99
+ }
100
+ }
101
+ end
102
+
103
+ def items_payload(line_items)
104
+ line_items.map do |item|
105
+ {
106
+ quantity: item.quantity,
107
+ unitPrice: amount(item.price),
108
+ name: item.name
109
+ }
110
+ end
111
+ end
112
+
113
+ def authorize_url
114
+ if preferred_test_mode
115
+ 'https://secure.snd.payu.com/pl/standard/user/oauth/authorize'
116
+ else
117
+ 'https://secure.payu.com/pl/standard/user/oauth/authorize'
118
+ end
119
+ end
120
+
121
+ def authorize
122
+ # https://developers.payu.com/europe/api#tag/Authorize/operation/oauth
123
+ conn = Faraday.new(url: authorize_url) do |faraday|
124
+ faraday.adapter Faraday.default_adapter
125
+ end
126
+
127
+ authorize_payload = {
128
+ grant_type: 'client_credentials',
129
+ client_id: preferred_payu_client_id&.to_i,
130
+ client_secret: preferred_payu_client_secret
131
+ }
132
+
133
+ response = conn.post do |req|
134
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
135
+ req.body = URI.encode_www_form(authorize_payload)
136
+ end
137
+
138
+ if response.success?
139
+ response_body = JSON.parse(response.body)
140
+ response_body['access_token']
141
+ else
142
+ Rails.logger.debug("Token: #{response.inspect}")
143
+ end
144
+ end
145
+
146
+ def verify_url(payu_order_id)
147
+ if preferred_test_mode
148
+ "https://secure.snd.payu.com/api/v2_1/orders/#{payu_order_id}/captures"
149
+ else
150
+ "https://secure.payu.com/api/v2_1/orders/#{payu_order_id}/captures"
151
+ end
152
+ end
153
+
154
+ def verify_transaction(status, payment, amount, currency, payu_order_id)
155
+ return false if status.blank? || payment.blank? || amount.blank? || currency.blank? || payu_order_id.blank?
156
+ return true if status == 'PENDING'
157
+ return false if !['WAITING_FOR_CONFIRMATION', 'COMPLETED', 'CANCELED'].include?(status)
158
+
159
+ float_amount = (amount.to_f / 100).to_f
160
+
161
+ if status == 'WAITING_FOR_CONFIRMATION'
162
+ conn = Faraday.new(url: verify_url(payu_order_id)) do |faraday|
163
+ faraday.adapter Faraday.default_adapter
164
+ faraday.request :authorization, 'Bearer', authorize
165
+ end
166
+
167
+ response = conn.post do |req|
168
+ req.headers['Content-Type'] = 'application/json'
169
+ end
170
+
171
+ if response.success?
172
+ return true
173
+ else
174
+ Rails.logger.warn("Verify_transaction #{payment.order.id} failed => #{response.inspect}")
175
+ return false
176
+ end
177
+ end
178
+
179
+ if payment.can_complete? && status == 'COMPLETED'
180
+ payment.amount = float_amount
181
+ payment.complete
182
+ elsif status == 'CANCELED'
183
+ payment.cancel!
184
+ end
185
+
186
+ private_metadata = payment.private_metadata
187
+ private_metadata[:order_id] = payu_order_id
188
+ private_metadata[:currency] = currency
189
+ private_metadata[:amount] = amount
190
+ payment.update(private_metadata: private_metadata)
191
+ true
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,18 @@
1
+ module Spree::Payment::ProcessingDecorator
2
+ def cancel!
3
+ if payment_method.is_a? Spree::Gateway::Payu
4
+ cancel_with_payu
5
+ else
6
+ super
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def cancel_with_payu
13
+ response = payment_method.cancel(id)
14
+ handle_response(response, :void, :failure)
15
+ end
16
+ end
17
+
18
+ Spree::Payment.include(Spree::Payment::ProcessingDecorator)
@@ -0,0 +1,18 @@
1
+
2
+ module Spree
3
+ module PaymentDecorator
4
+ def self.prepended(base)
5
+ base.after_create :create_payu_token
6
+ end
7
+
8
+ private
9
+
10
+ def create_payu_token
11
+ return if not self.payment_method.is_a? Gateway::Payu
12
+
13
+ payment_method.register_order(self.order, self.id, self.payment_method.id)
14
+ end
15
+ end
16
+ end
17
+
18
+ ::Spree::Payment.prepend(Spree::PaymentDecorator)
@@ -0,0 +1,14 @@
1
+ <p>
2
+ <%= t('payu.info') %>
3
+ </p>
4
+ <p>
5
+ <%= t('payu.token') %>
6
+ <%= @payment&.public_metadata.fetch('token', '') %>
7
+ </p>
8
+
9
+ <p>
10
+ <% if @payment&.public_metadata.fetch('payment_url', '').present? %>
11
+ <%= t('payu.url') %>
12
+ <%= link_to @payment&.public_metadata.fetch('payment_url', ''), target: "_blank" %>
13
+ <% end %>
14
+ </p>
@@ -0,0 +1,34 @@
1
+ module SpreePayu
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ require 'deface'
5
+ engine_name 'spree_payu'
6
+
7
+ isolate_namespace SpreePayuGateway
8
+
9
+ config.autoload_paths += %W[#{config.root}/lib]
10
+
11
+ config.after_initialize do |app|
12
+ app.config.spree.payment_methods << Spree::Gateway::Payu
13
+ end
14
+
15
+ def self.activate
16
+ # if self.frontend_available?
17
+ # Dir.glob(File.join(File.dirname(__FILE__), '../../app/overrides/*.rb')) do |c|
18
+ # Rails.application.config.cache_classes ? require(c) : load(c)
19
+ # end
20
+ # end
21
+
22
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
23
+ Rails.configuration.cache_classes ? require(c) : load(c)
24
+ end
25
+ end
26
+
27
+ def self.frontend_available?
28
+ @@frontend_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('Spree::Frontend::Engine')
29
+ end
30
+
31
+ config.to_prepare &method(:activate).to_proc
32
+ end
33
+
34
+ end
@@ -0,0 +1,7 @@
1
+ module SpreePayuGateway
2
+ VERSION = '1.0.0'.freeze
3
+
4
+ def self.version
5
+ VERSION
6
+ end
7
+ end
data/lib/spree_payu.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'spree_core'
2
+ require 'spree_payu/version'
3
+ require 'spree_payu/engine'
4
+ require 'spree_extension'
5
+
6
+ module SpreePayuGateway
7
+ end
@@ -0,0 +1,25 @@
1
+ namespace :spree_payu_payment do
2
+ desc "Copies all migrations and assets (NOTE: This will be obsolete with Rails 3.1)"
3
+ task :install do
4
+ Rake::Task['spree_payu_payment:install:migrations'].invoke
5
+ Rake::Task['spree_payu_payment:install:assets'].invoke
6
+ end
7
+
8
+ namespace :install do
9
+ desc "Copies all migrations (NOTE: This will be obsolete with Rails 3.1)"
10
+ task :migrations do
11
+ source = File.join(File.dirname(__FILE__), '..', '..', 'db')
12
+ destination = File.join(Rails.root, 'db')
13
+ Spree::FileUtilz.mirror_files(source, destination)
14
+ end
15
+
16
+ desc "Copies all assets (NOTE: This will be obsolete with Rails 3.1)"
17
+ task :assets do
18
+ source = File.join(File.dirname(__FILE__), '..', '..', 'public')
19
+ destination = File.join(Rails.root, 'public')
20
+ puts "INFO: Mirroring assets from #{source} to #{destination}"
21
+ Spree::FileUtilz.mirror_files(source, destination)
22
+ end
23
+ end
24
+
25
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_payu
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Cloud Sailor AS
8
+ - Cloud Sailor Sp. z o.o.
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2024-07-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: deface
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: faraday
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: openssl
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: spree_backend
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 4.6.0
63
+ - - "<"
64
+ - !ruby/object:Gem::Version
65
+ version: '5.0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 4.6.0
73
+ - - "<"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: spree_core
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 4.6.0
83
+ - - "<"
84
+ - !ruby/object:Gem::Version
85
+ version: '5.0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 4.6.0
93
+ - - "<"
94
+ - !ruby/object:Gem::Version
95
+ version: '5.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: spree_auth_devise
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: bigdecimal
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ description: PayU payment gateway for Spree
125
+ email: support@cloudsailor.com
126
+ executables: []
127
+ extensions: []
128
+ extra_rdoc_files: []
129
+ files:
130
+ - LICENSE
131
+ - README.md
132
+ - app/controllers/spree/gateway/payu_controller.rb
133
+ - app/models/spree/gateway/payu.rb
134
+ - app/models/spree/payment/processing_decorator.rb
135
+ - app/models/spree/payment_decorator.rb
136
+ - app/views/spree/admin/payments/source_views/_payu.html.erb
137
+ - lib/spree_payu.rb
138
+ - lib/spree_payu/engine.rb
139
+ - lib/spree_payu/version.rb
140
+ - lib/tasks/install.rake
141
+ homepage: https://github.com/cloudsailor/spree_payu
142
+ licenses:
143
+ - BSD-3-Clause
144
+ metadata:
145
+ bug_tracker_uri: https://github.com/cloudsailor/spree_payu/issues
146
+ changelog_uri: https://github.com/cloudsailor/spree_payu/releases/tag/v1.0.0
147
+ source_code_uri: https://github.com/cloudsailor/spree_payu/tree/v1.0.0
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '3.0'
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ requirements:
163
+ - none
164
+ rubygems_version: 3.5.7
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: PayU payment gateway for Spree
168
+ test_files: []