spree_payu 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []