payanyway 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +47 -15
- data/config/routes.rb +7 -4
- data/lib/payanyway.rb +4 -0
- data/lib/payanyway/controller.rb +45 -10
- data/lib/payanyway/gateway.rb +12 -1
- data/lib/payanyway/helpers/signature_generator.rb +22 -20
- data/lib/payanyway/{helpers → request}/payment_url.rb +19 -9
- data/lib/payanyway/response/base.rb +5 -1
- data/lib/payanyway/response/check.rb +113 -0
- data/lib/payanyway/response/pay.rb +3 -2
- data/lib/payanyway/version.rb +1 -1
- data/payanyway.gemspec +1 -0
- data/spec/config/routes_spec.rb +15 -0
- data/spec/controllers/payanyway_controller_spec.rb +36 -3
- data/spec/lib/payanyway/gateway_spec.rb +1 -1
- data/spec/lib/payanyway/request/payment_url_spec.rb +43 -0
- data/spec/lib/payanyway/response/base_spec.rb +11 -0
- data/spec/lib/payanyway/response/check_spec.rb +102 -0
- data/spec/lib/payanyway/response/pay_spec.rb +3 -14
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3820acaab28d06906f0eb283beda473a1d2f084
|
4
|
+
data.tar.gz: 0e8f58b5347aea043c3ee8a17fc346afbb078d43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbb2e8812b3d2d95fb3cdc87d23ad119600c763fffc19891980a9e5c681aeabfffa8ed63921b1ca9cae8d00aa8a7b301276951a671819b116eb539de353cb542
|
7
|
+
data.tar.gz: 8b4c4f979d6adf934788cb5fbf542b9288615cffd32c720f5682022b4f6ef66d49c94bf51802865acc23973460c646b1648a78b931c2b062041171faa9dc3caf
|
data/README.md
CHANGED
@@ -44,7 +44,7 @@ class PayanywayController
|
|
44
44
|
|
45
45
|
def pay_implementation(params)
|
46
46
|
# вызывается при оповещении магазина об
|
47
|
-
# успешной оплате пользователем заказа.
|
47
|
+
# успешной оплате пользователем заказа. (Pay URL)
|
48
48
|
#
|
49
49
|
# params[ KEY ], где KEY ∈ [ :moneta_id, :order_id, :operation_id,
|
50
50
|
# :amount, :currency, :subscriber_id, :test_mode, :user, :corraccount,
|
@@ -54,6 +54,23 @@ class PayanywayController
|
|
54
54
|
def fail_implementation(order_id)
|
55
55
|
# вызывается при отправки шлюзом пользователя на Fail URL.
|
56
56
|
end
|
57
|
+
|
58
|
+
def return_implementation(order_id)
|
59
|
+
# Вызывается при добровольном отказе пользователем от оплаты (Return URL)
|
60
|
+
end
|
61
|
+
|
62
|
+
def in_progress_implementation(order_id)
|
63
|
+
# Вызывается после успешного запроса на авторизацию средств,
|
64
|
+
# до подтверждения списания и зачисления средств (InProgress URL)
|
65
|
+
#
|
66
|
+
# ВНИМАНИЕ: InProgress URL может быть использован в любом способе оплаты.
|
67
|
+
# Если к моменту, когда пользователя надо вернуть в магазин оплата
|
68
|
+
# по какой-либо причине не завершена, то его перекинет на InProgress, если он указан.
|
69
|
+
# Если InProgress URL не указан, то на Success URL.
|
70
|
+
# Если операция уже успешно выполнилась, то сразу на Success URL.
|
71
|
+
# К примеру, в случае с картами чаще всего получается так, что операция не успевает выполниться,
|
72
|
+
# поэтому InProgress будет использован с бОльшей вероятностью, чем Success URL.
|
73
|
+
end
|
57
74
|
end
|
58
75
|
```
|
59
76
|
|
@@ -73,7 +90,12 @@ production: <<: *config
|
|
73
90
|
```
|
74
91
|
## Использование
|
75
92
|
|
76
|
-
Что бы получить ссылку на платежный шлюз для оплаты заказа пользвателем,
|
93
|
+
Что бы получить ссылку на платежный шлюз для оплаты заказа пользвателем,
|
94
|
+
используйте `Payanyway::Gateway.payment_url(params, use_signature = true)`, где `params[ KEY ]` такой, что `KEY` ∈
|
95
|
+
`[:order_id, :amount, :test_mode, :description, :subscriber_id, :custom1, :custom2, :custom3, :locale, :payment_system_unit_id, :payment_system_limit_ids]`
|
96
|
+
|
97
|
+
Если в настройках счета в системе **moneta.ru** выставлен флаг «Можно переопределять настройки в URL», то можно так же передавать
|
98
|
+
`[:success_url, :in_progress_url, :fail_url, :return_url]`.
|
77
99
|
|
78
100
|
Пример:
|
79
101
|
```ruby
|
@@ -84,7 +106,9 @@ class OrdersController < AplicationController
|
|
84
106
|
order = Order.create(params[:order])
|
85
107
|
redirect_to Payanyway::Gateway.payment_url(
|
86
108
|
order_id: order.id,
|
87
|
-
amount: order.total_amount
|
109
|
+
amount: order.total_amount,
|
110
|
+
locale: 'ru',
|
111
|
+
description: "Оплата заказа № #{ order.number } на сумму #{ order.total_amount }руб."
|
88
112
|
)
|
89
113
|
end
|
90
114
|
end
|
@@ -93,18 +117,26 @@ end
|
|
93
117
|
### Расшифровка параметров
|
94
118
|
|
95
119
|
params[ KEY ], где KEY | Описание
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
120
|
+
---------------------------|:-----------------------------------------------------------
|
121
|
+
`:moneta_id` | Идентификатор магазина в системе MONETA.RU.
|
122
|
+
`:order_id` | Внутренний идентификатор заказа, однозначно определяющий заказ в магазине.
|
123
|
+
`:operation_id` | Номер операции в системе MONETA.RU.
|
124
|
+
`:amount` | Фактическая сумма, полученная на оплату заказа.
|
125
|
+
`:currency` | ISO код валюты, в которой произведена оплата заказа в магазине.
|
126
|
+
`:test_mode` | Флаг оплаты в тестовом режиме (1 - да, 0 - нет).
|
127
|
+
`:description` | Описание оплаты.
|
128
|
+
`:subscriber_id` | Внутренний идентификатор пользователя в системе магазина.
|
129
|
+
`:corraccount` | Номер счета плательщика.
|
130
|
+
`:custom[1|2|3]` | Поля произвольных параметров. Будут возращены магазину в параметрах отчета о проведенной оплате.
|
131
|
+
`:user` | Номер счета пользователя, если оплата производилась с пользовательского счета в системе «MONETA.RU».MONETA.Assistant.
|
132
|
+
`:locale` | (ru\|en) Язык пользовательского интерфейса.
|
133
|
+
`:payment_system_unit_id` | Предварительный выбор платежной системы. (https://www.moneta.ru/viewPaymentMethods.htm)
|
134
|
+
`:payment_system_limit_ids`| Список (разделенный запятыми) идентификаторов платежных систем, которые необходимо показывать пользователю.
|
135
|
+
`:success_url` | URL страницы магазина, куда должен попасть покупатель после успешно выполненных действий.
|
136
|
+
`:in_progress_url` | URL страницы магазина, куда должен попасть покупатель после успешного запроса на авторизацию средств, до подтверждения списания и зачисления средств.
|
137
|
+
`:fail_url` | URL страницы магазина, куда должен попасть покупатель после отмененной или неуспешной оплаты.
|
138
|
+
`:return_url` | URL страницы магазина, куда должен вернуться покупатель при добровольном отказе от оплаты.
|
139
|
+
|
108
140
|
|
109
141
|
## Contributing
|
110
142
|
|
data/config/routes.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
Payanyway::Engine.routes.draw do
|
2
|
-
get 'success'
|
3
|
-
get 'pay'
|
4
|
-
get 'fail'
|
5
|
-
|
2
|
+
get 'success' => 'payanyway#success', as: :payanyway_on_success
|
3
|
+
get 'pay' => 'payanyway#pay', as: :payanyway_pay
|
4
|
+
get 'fail' => 'payanyway#fail', as: :payanyway_on_fail
|
5
|
+
get 'return' => 'payanyway#return', as: :payanyway_on_return
|
6
|
+
get 'in_progress' => 'payanyway#in_progress', as: :payanyway_in_progress
|
7
|
+
get 'check' => 'payanyway#check', as: :payanyway_on_check
|
8
|
+
end
|
data/lib/payanyway.rb
CHANGED
@@ -4,6 +4,10 @@ require 'active_support/core_ext'
|
|
4
4
|
require 'payanyway/helpers/settings'
|
5
5
|
require 'payanyway/helpers/signature_generator'
|
6
6
|
|
7
|
+
require 'payanyway/request/payment_url'
|
8
|
+
require 'payanyway/response/base'
|
9
|
+
require 'payanyway/response/pay'
|
10
|
+
|
7
11
|
require 'payanyway/gateway'
|
8
12
|
require 'payanyway/engine'
|
9
13
|
require 'payanyway/controller'
|
data/lib/payanyway/controller.rb
CHANGED
@@ -8,49 +8,84 @@ module Payanyway
|
|
8
8
|
|
9
9
|
def pay
|
10
10
|
service = Payanyway::Response::Pay.new(params)
|
11
|
-
service.perform
|
12
11
|
service.success? ?
|
13
12
|
pay_implementation(service.pretty_params) :
|
14
|
-
|
13
|
+
Rails.logger.error(service.error_message)
|
15
14
|
|
16
15
|
render text: service.result
|
17
16
|
end
|
18
17
|
|
19
18
|
def success
|
20
19
|
service = Payanyway::Response::Base.new(params)
|
21
|
-
Rails.logger.info("Called success payment url for order '#{ service.pretty_params[:order_id] }'")
|
22
20
|
|
23
21
|
success_implementation(service.pretty_params)
|
24
22
|
end
|
25
23
|
|
26
24
|
def fail
|
27
25
|
service = Payanyway::Response::Base.new(params)
|
28
|
-
Rails.logger.error("Fail paid order '#{ service.pretty_params[:order_id] }'")
|
29
26
|
|
30
|
-
fail_implementation(service.pretty_params)
|
27
|
+
fail_implementation(service.pretty_params[:order_id])
|
31
28
|
end
|
32
29
|
|
33
|
-
|
30
|
+
def return
|
31
|
+
service = Payanyway::Response::Base.new(params)
|
32
|
+
|
33
|
+
return_implementation(service.pretty_params[:order_id])
|
34
|
+
end
|
35
|
+
|
36
|
+
def in_progress
|
37
|
+
service = Payanyway::Response::Base.new(params)
|
34
38
|
|
35
|
-
|
36
|
-
Rails.logger.error("ERROR! Invalid signature for order #{ params[:order_id] }. Params: #{ params.inspect }")
|
39
|
+
in_progress_implementation(service.pretty_params[:order_id])
|
37
40
|
end
|
38
41
|
|
42
|
+
def check
|
43
|
+
service = Payanyway::Response::Check.new(params)
|
44
|
+
raise service.error_message unless service.success?
|
45
|
+
|
46
|
+
render xml: service.result(check_implementation(service.pretty_params)).to_xml
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
39
51
|
def pay_implementation(params)
|
40
52
|
# Вызывается после успешного прохождения
|
41
53
|
# запроса об оплате от payanyway.ru
|
42
54
|
|
43
|
-
Rails.logger.info("Success paid order #{ params[:order_id] }")
|
55
|
+
Rails.logger.info("PAYANYWAY: Success paid order #{ params[:order_id] }")
|
44
56
|
end
|
45
57
|
|
46
58
|
def success_implementation(params)
|
47
59
|
# Вызывается после успешной оплаты
|
60
|
+
|
61
|
+
Rails.logger.info("PAYANYWAY: Called success payment url for order '#{ params[:order_id] }'")
|
48
62
|
render nothing: true
|
49
63
|
end
|
50
64
|
|
51
|
-
def fail_implementation(
|
65
|
+
def fail_implementation(order_id)
|
52
66
|
# Вызывается после ошибки при оплате
|
67
|
+
|
68
|
+
Rails.logger.error("PAYANYWAY: Fail paid order '#{ order_id }'")
|
69
|
+
render nothing: true
|
70
|
+
end
|
71
|
+
|
72
|
+
def return_implementation(order_id)
|
73
|
+
# Вызывается при добровольном отказе пользователем от оплаты
|
74
|
+
|
75
|
+
Rails.logger.info("PAYANYWAY: Return from payanyway. Order '#{ order_id }'")
|
53
76
|
render nothing: true
|
54
77
|
end
|
78
|
+
|
79
|
+
def in_progress_implementation(order_id)
|
80
|
+
# Вызывается после успешного запроса на авторизацию средств, до подтверждения списания и зачисления средств
|
81
|
+
|
82
|
+
Rails.logger.info("PAYANYWAY: Order '#{ order_id }' in progress")
|
83
|
+
render nothing: true
|
84
|
+
end
|
85
|
+
|
86
|
+
def check_implementation(params)
|
87
|
+
# Ответ на запрос о проверке заказа
|
88
|
+
# { amount: AMOUNT, state: STATE, description: description, attributes: ATTRIBUTES }
|
89
|
+
end
|
55
90
|
end
|
56
91
|
end
|
data/lib/payanyway/gateway.rb
CHANGED
@@ -35,8 +35,19 @@ module Payanyway
|
|
35
35
|
# * _params[custom1]_ - Поля произвольных параметров.
|
36
36
|
# * _params[custom2]_ - Поля произвольных параметров.
|
37
37
|
# * _params[custom3]_ - Поля произвольных параметров.
|
38
|
+
# * _params[locale]_ - (ru|en) Язык пользовательского интерфейса.
|
38
39
|
|
39
|
-
|
40
|
+
# * _params[payment_system_unit_id ]_ - Предварительный выбор платежной системы. (https://www.moneta.ru/viewPaymentMethods.htm)
|
41
|
+
# * _params[payment_system_limit_ids ]_ - Список (разделенный запятыми) идентификаторов платежных систем, которые необходимо показывать пользователю. (https://www.moneta.ru/viewPaymentMethods.htm)
|
42
|
+
|
43
|
+
# Эти параметры используются только тогда, когда в настройках счета выставлен флаг «Можно переопределять настройки в URL»
|
44
|
+
# * _params[success_url]_ - URL страницы магазина, куда должен попасть покупатель после успешно выполненных действий.
|
45
|
+
# * _params[inprogress_url]_- URL страницы магазина, куда должен попасть покупатель после успешного запроса на авторизацию средств, до подтверждения списания и зачисления средств.
|
46
|
+
# * _params[fail_url]_ - URL страницы магазина, куда должен попасть покупатель после отмененной или неуспешной оплаты.
|
47
|
+
# * _params[return_url]_ - URL страницы магазина, куда должен вернуться покупатель при добровольном отказе от оплаты.
|
48
|
+
|
49
|
+
|
50
|
+
Payanyway::Request::PaymentUrl.build(params, use_signature)
|
40
51
|
end
|
41
52
|
|
42
53
|
class << self
|
@@ -3,27 +3,29 @@ require 'digest/md5'
|
|
3
3
|
module Payanyway
|
4
4
|
module Helpers
|
5
5
|
class SignatureGenerate
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
generate_by(params,
|
6
|
+
BASE_KEYS = %w(
|
7
|
+
MNT_TRANSACTION_ID
|
8
|
+
MNT_OPERATION_ID
|
9
|
+
MNT_AMOUNT
|
10
|
+
MNT_CURRENCY_CODE
|
11
|
+
MNT_SUBSCRIBER_ID
|
12
|
+
MNT_TEST_MODE
|
13
|
+
)
|
14
|
+
|
15
|
+
KEYS = {
|
16
|
+
pay: BASE_KEYS,
|
17
|
+
url: BASE_KEYS - [ 'MNT_OPERATION_ID' ],
|
18
|
+
check: [ 'MNT_COMMAND' ] + BASE_KEYS,
|
19
|
+
check_response: %w(MNT_RESULT_CODE MNT_ID MNT_TRANSACTION_ID)
|
20
|
+
}
|
21
|
+
|
22
|
+
KEYS.each do |key_name, keys|
|
23
|
+
define_singleton_method("for_#{ key_name }") do |params|
|
24
|
+
generate_by(params, keys)
|
25
25
|
end
|
26
|
+
end
|
26
27
|
|
28
|
+
class << self
|
27
29
|
private
|
28
30
|
|
29
31
|
def generate_by(params, keys)
|
@@ -33,7 +35,7 @@ module Payanyway
|
|
33
35
|
end
|
34
36
|
|
35
37
|
def get_value(params, key)
|
36
|
-
(key == 'MNT_AMOUNT') ? '%.2f' % params[key] : params[key]
|
38
|
+
(key == 'MNT_AMOUNT' && params[key].present?) ? '%.2f' % params[key] : params[key]
|
37
39
|
end
|
38
40
|
|
39
41
|
def md5(str)
|
@@ -1,15 +1,25 @@
|
|
1
1
|
module Payanyway
|
2
|
-
module
|
2
|
+
module Request
|
3
3
|
class PaymentUrl
|
4
4
|
PARAMS = {
|
5
|
-
'MNT_TRANSACTION_ID'
|
6
|
-
'
|
7
|
-
'
|
8
|
-
'
|
9
|
-
|
10
|
-
'
|
11
|
-
'
|
12
|
-
|
5
|
+
'MNT_TRANSACTION_ID' => :order_id,
|
6
|
+
'MNT_AMOUNT' => :amount,
|
7
|
+
'MNT_DESCRIPTION' => :description,
|
8
|
+
'MNT_SUBSCRIBER_ID' => :subscriber_id,
|
9
|
+
|
10
|
+
'MNT_SUCCESS_URL' => :success_url,
|
11
|
+
'MNT_INPROGRESS_URL' => :in_progress_url,
|
12
|
+
'MNT_FAIL_URL' => :fail_url,
|
13
|
+
'MNT_RETURN_URL' => :return_url,
|
14
|
+
|
15
|
+
'MNT_CUSTOM1' => :custom1,
|
16
|
+
'MNT_CUSTOM2' => :custom2,
|
17
|
+
'MNT_CUSTOM3' => :custom3,
|
18
|
+
|
19
|
+
'moneta.locale' => :locale,
|
20
|
+
'paymentSystem.unitId' => :payment_system_unit_id,
|
21
|
+
'paymentSystem.limitIds' => :payment_system_limit_ids
|
22
|
+
# 'MNT_SIGNATURE' => добавляется при use_signature == true
|
13
23
|
}.to_settings
|
14
24
|
|
15
25
|
class << self
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Payanyway
|
2
|
+
module Response
|
3
|
+
class InvalidState < Exception; end
|
4
|
+
|
5
|
+
class Check < Base
|
6
|
+
RESPONSE_CODE = {
|
7
|
+
set_amount: 100,
|
8
|
+
paid: 200,
|
9
|
+
in_process: 302,
|
10
|
+
unpaid: 402,
|
11
|
+
canceled: 500
|
12
|
+
}
|
13
|
+
|
14
|
+
@@_params = {
|
15
|
+
'MNT_COMMAND' => :command,
|
16
|
+
'MNT_ID' => :moneta_id,
|
17
|
+
'MNT_TRANSACTION_ID' => :order_id,
|
18
|
+
'MNT_OPERATION_ID' => :operation_id,
|
19
|
+
'MNT_AMOUNT' => :amount,
|
20
|
+
'MNT_CURRENCY_CODE' => :currency,
|
21
|
+
'MNT_SUBSCRIBER_ID' => :subscriber_id,
|
22
|
+
'MNT_TEST_MODE' => :test_mode,
|
23
|
+
'MNT_SIGNATURE' => :signature,
|
24
|
+
'MNT_USER' => :user,
|
25
|
+
'MNT_CORRACCOUNT' => :corraccount,
|
26
|
+
'MNT_CUSTOM1' => :custom1,
|
27
|
+
'MNT_CUSTOM2' => :custom2,
|
28
|
+
'MNT_CUSTOM3' => :custom3,
|
29
|
+
'paymentSystem.unitId' => :payment_system_unit_id
|
30
|
+
}.invert.to_settings
|
31
|
+
|
32
|
+
def initialize(params)
|
33
|
+
super
|
34
|
+
@valid_signature = (@pretty_params[:signature] == Payanyway::Helpers::SignatureGenerate.for_check(@params))
|
35
|
+
end
|
36
|
+
|
37
|
+
def success?
|
38
|
+
@valid_signature
|
39
|
+
end
|
40
|
+
|
41
|
+
def result(attr)
|
42
|
+
# Возвращает Nokogiri::XML документ
|
43
|
+
# * _attr[:amount] - сумма заказа
|
44
|
+
# * _attr[:state] - статус платежа(см. RESPONSE_CODE)
|
45
|
+
# * _attr[:description] - Произвольное описание заказа (необязятельно)
|
46
|
+
# * _attr[:attributes] - Произвольный атрибуты заказа (необязятельно)
|
47
|
+
|
48
|
+
validate_status!(attr[:state])
|
49
|
+
|
50
|
+
xml = base_xml(attr[:amount], attr[:state], attr[:description])
|
51
|
+
parent = xml.at_css('MNT_RESPONSE')
|
52
|
+
|
53
|
+
parent.add_child(signature_node(xml))
|
54
|
+
parent.add_child(attributes_node(attr[:attributes], xml)) if attr[:attributes].present?
|
55
|
+
|
56
|
+
xml
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def validate_status!(status)
|
62
|
+
if RESPONSE_CODE.keys.exclude?(status.to_sym)
|
63
|
+
raise InvalidState.new("PAYANYWAY: Invalid response state! State must be eq #{ RESPONSE_CODE.keys }")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def base_xml(amount, state, description)
|
68
|
+
xml = <<-EOXML
|
69
|
+
<MNT_RESPONSE>
|
70
|
+
<MNT_ID>#{ Payanyway::Gateway.config['moneta_id'] }</MNT_ID>
|
71
|
+
<MNT_TRANSACTION_ID>#{ @pretty_params[:order_id] }</MNT_TRANSACTION_ID>
|
72
|
+
<MNT_RESULT_CODE>#{ result_code_of(amount, state) }</MNT_RESULT_CODE>
|
73
|
+
<MNT_DESCRIPTION>#{ description }</MNT_DESCRIPTION>
|
74
|
+
<MNT_AMOUNT>#{ amount }</MNT_AMOUNT>
|
75
|
+
</MNT_RESPONSE>
|
76
|
+
EOXML
|
77
|
+
|
78
|
+
Nokogiri::XML(xml, nil, 'UTF-8')
|
79
|
+
end
|
80
|
+
|
81
|
+
def signature_node(xml)
|
82
|
+
create_new_node('MNT_SIGNATURE',
|
83
|
+
Payanyway::Helpers::SignatureGenerate.for_check_response(Hash.from_xml(xml.to_s)['MNT_RESPONSE']),
|
84
|
+
xml
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def result_code_of(amount, state)
|
89
|
+
if @pretty_params[:amount].blank? && amount.present?
|
90
|
+
RESPONSE_CODE[:set_amount]
|
91
|
+
else
|
92
|
+
RESPONSE_CODE[state.to_sym]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def attributes_node(attributes, xml)
|
97
|
+
Nokogiri::XML::Node.new('MNT_ATTRIBUTES', xml).tap do |node|
|
98
|
+
attributes.each do |key, value|
|
99
|
+
attr_node = Nokogiri::XML::Node.new('ATTRIBUTE', xml)
|
100
|
+
attr_node.add_child(create_new_node('KEY', key.to_s, xml))
|
101
|
+
attr_node.add_child(create_new_node('VALUE', value.to_s, xml))
|
102
|
+
|
103
|
+
node.add_child(attr_node)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_new_node(name, content, xml)
|
109
|
+
Nokogiri::XML::Node.new(name, xml).tap { |node| node.content = content }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -17,8 +17,9 @@ module Payanyway
|
|
17
17
|
'MNT_CUSTOM3' => :custom3
|
18
18
|
}.invert.to_settings
|
19
19
|
|
20
|
-
def
|
21
|
-
|
20
|
+
def initialize(params)
|
21
|
+
super
|
22
|
+
@valid_signature = (@pretty_params[:signature] == Payanyway::Helpers::SignatureGenerate.for_pay(@params))
|
22
23
|
end
|
23
24
|
|
24
25
|
def success?
|
data/lib/payanyway/version.rb
CHANGED
data/payanyway.gemspec
CHANGED
data/spec/config/routes_spec.rb
CHANGED
@@ -16,5 +16,20 @@ describe Payanyway::Engine, type: :routing do
|
|
16
16
|
controller: 'payanyway',
|
17
17
|
action: 'fail'
|
18
18
|
)}
|
19
|
+
|
20
|
+
specify { get('/return').should route_to(
|
21
|
+
controller: 'payanyway',
|
22
|
+
action: 'return'
|
23
|
+
)}
|
24
|
+
|
25
|
+
specify { get('/in_progress').should route_to(
|
26
|
+
controller: 'payanyway',
|
27
|
+
action: 'in_progress'
|
28
|
+
)}
|
29
|
+
|
30
|
+
specify { get('/check').should route_to(
|
31
|
+
controller: 'payanyway',
|
32
|
+
action: 'check'
|
33
|
+
)}
|
19
34
|
end
|
20
35
|
end
|
@@ -3,7 +3,7 @@ describe PayanywayController do
|
|
3
3
|
|
4
4
|
describe 'GET #success' do
|
5
5
|
it 'should and message to logger' do
|
6
|
-
expect(Rails.logger).to receive(:info).with("Called success payment url for order '676'")
|
6
|
+
expect(Rails.logger).to receive(:info).with("PAYANYWAY: Called success payment url for order '676'")
|
7
7
|
|
8
8
|
get :success, { 'MNT_TRANSACTION_ID' => 676 }
|
9
9
|
end
|
@@ -11,9 +11,42 @@ describe PayanywayController do
|
|
11
11
|
|
12
12
|
describe 'GET #fail' do
|
13
13
|
it 'should and message to logger' do
|
14
|
-
expect(Rails.logger).to receive(:error).with("Fail paid order '676'")
|
14
|
+
expect(Rails.logger).to receive(:error).with("PAYANYWAY: Fail paid order '676'")
|
15
15
|
|
16
16
|
get :fail, { 'MNT_TRANSACTION_ID' => 676 }
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
|
+
describe 'GET #return' do
|
21
|
+
it 'should and message to logger' do
|
22
|
+
expect(Rails.logger).to receive(:info).with("PAYANYWAY: Return from payanyway. Order '676'")
|
23
|
+
|
24
|
+
get :return, { 'MNT_TRANSACTION_ID' => 676 }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'GET #in_progress' do
|
29
|
+
it 'should and message to logger' do
|
30
|
+
expect(Rails.logger).to receive(:info).with("PAYANYWAY: Order '676' in progress")
|
31
|
+
|
32
|
+
get :in_progress, { 'MNT_TRANSACTION_ID' => 676 }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'GET #check' do
|
37
|
+
context 'when invalid signature' do
|
38
|
+
it 'should raise error' do
|
39
|
+
expect{ get :check }.to raise_error
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when valid signature' do
|
44
|
+
it 'should and message to logger' do
|
45
|
+
expect_any_instance_of(Payanyway::Controller).to receive(:check_implementation).and_return({amount: 12, state: :paid})
|
46
|
+
get :check, { 'MNT_TRANSACTION_ID' => 676, 'MNT_SIGNATURE' => '79c1c4f41a0a70bb107c976ebba25811' }
|
47
|
+
|
48
|
+
expect(Nokogiri::XML(response.body).at_css('MNT_RESPONSE')).to be_present
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
describe Payanyway::Request::PaymentUrl do
|
4
|
+
subject do
|
5
|
+
parse_url = URI(described_class.build(params, false))
|
6
|
+
|
7
|
+
Hash[ URI.decode_www_form(parse_url.query) ]
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'when reset urls' do
|
11
|
+
let(:params) {
|
12
|
+
{
|
13
|
+
amount: '120.25',
|
14
|
+
order_id: 'FF790ABCD',
|
15
|
+
success_url: 'success_url',
|
16
|
+
in_progress_url: 'in_progress_url',
|
17
|
+
fail_url: 'fail_url',
|
18
|
+
return_url: 'return_url'
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
its(['MNT_SUCCESS_URL']) { is_expected.to eq('success_url') }
|
23
|
+
its(['MNT_INPROGRESS_URL']) { is_expected.to eq('in_progress_url') }
|
24
|
+
its(['MNT_FAIL_URL']) { is_expected.to eq('fail_url') }
|
25
|
+
its(['MNT_RETURN_URL']) { is_expected.to eq('return_url') }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when set payment params' do
|
29
|
+
let(:params) {
|
30
|
+
{
|
31
|
+
amount: '120.25',
|
32
|
+
order_id: 'FF790ABCD',
|
33
|
+
locale: 'ru',
|
34
|
+
payment_system_unit_id: '1015',
|
35
|
+
payment_system_limit_ids: '1015,1017'
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
its(['moneta.locale']) { is_expected.to eq('ru') }
|
40
|
+
its(['paymentSystem.unitId']) { is_expected.to eq('1015') }
|
41
|
+
its(['paymentSystem.limitIds']) { is_expected.to eq('1015,1017') }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
describe Payanyway::Response::Base do
|
2
|
+
let(:service) { described_class.new(params) }
|
3
|
+
let(:params) { { 'MNT_ID' => 1, 'MNT_AMOUNT' => 10.20 } }
|
4
|
+
|
5
|
+
describe '#pretty_params' do
|
6
|
+
subject { service.pretty_params }
|
7
|
+
|
8
|
+
its([:moneta_id]) { is_expected.to eq(1) }
|
9
|
+
its([:amount]) { is_expected.to eq(10.20) }
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
describe Payanyway::Response::Check do
|
2
|
+
RSpec::Matchers.define :be_eq_node do |expected|
|
3
|
+
def value_of(node)
|
4
|
+
elements = node.children.select { |e| e.instance_of? Nokogiri::XML::Element }
|
5
|
+
elements.map { |el| [ el.name, el.children.many? ? value_of(el) : el.children.text ] }
|
6
|
+
end
|
7
|
+
|
8
|
+
match do |actual|
|
9
|
+
value_of(actual) == value_of(expected)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:service) { described_class.new(params) }
|
14
|
+
let(:request_amount) { 10.20 }
|
15
|
+
let(:params) do
|
16
|
+
{
|
17
|
+
'MNT_TRANSACTION_ID' => 2,
|
18
|
+
'MNT_OPERATION_ID' => 3,
|
19
|
+
'MNT_AMOUNT' => request_amount,
|
20
|
+
'MNT_CURRENCY_CODE' => 'RUB',
|
21
|
+
'MNT_TEST_MODE' => 1
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#result' do
|
26
|
+
let(:xml) { service.result(amount: amount, state: state, description: description, attributes: attributes) }
|
27
|
+
let(:amount) { request_amount }
|
28
|
+
let(:state) { :unpaid }
|
29
|
+
let(:description) { 'Заказ создан, но не оплачен' }
|
30
|
+
let(:attributes) { {} }
|
31
|
+
|
32
|
+
context 'when code 402' do
|
33
|
+
it 'should valid xml' do
|
34
|
+
expected = <<-EOXML
|
35
|
+
<MNT_RESPONSE>
|
36
|
+
<MNT_ID>141290</MNT_ID>
|
37
|
+
<MNT_TRANSACTION_ID>2</MNT_TRANSACTION_ID>
|
38
|
+
<MNT_RESULT_CODE>402</MNT_RESULT_CODE>
|
39
|
+
<MNT_DESCRIPTION>Заказ создан, но не оплачен</MNT_DESCRIPTION>
|
40
|
+
<MNT_AMOUNT>10.2</MNT_AMOUNT>
|
41
|
+
<MNT_SIGNATURE>061c2b859c27c75db9e5bbef3aef90a0</MNT_SIGNATURE>
|
42
|
+
</MNT_RESPONSE>
|
43
|
+
EOXML
|
44
|
+
expected_xml = Nokogiri::XML(expected, nil, 'UTF-8')
|
45
|
+
|
46
|
+
expect(xml.at_css('MNT_RESPONSE')).to be_eq_node(expected_xml.at_css('MNT_RESPONSE'))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when have attributes' do
|
51
|
+
let(:attributes) { { type: 'chocolate', brand: 'Mars' } }
|
52
|
+
|
53
|
+
it 'should generate attributes xml' do
|
54
|
+
expected = <<-EOXML
|
55
|
+
<MNT_RESPONSE>
|
56
|
+
<MNT_ATTRIBUTES>
|
57
|
+
<ATTRIBUTE>
|
58
|
+
<KEY>type</KEY>
|
59
|
+
<VALUE>chocolate</VALUE>
|
60
|
+
</ATTRIBUTE>
|
61
|
+
<ATTRIBUTE>
|
62
|
+
<KEY>brand</KEY>
|
63
|
+
<VALUE>Mars</VALUE>
|
64
|
+
</ATTRIBUTE>
|
65
|
+
</MNT_ATTRIBUTES>
|
66
|
+
</MNT_RESPONSE>
|
67
|
+
EOXML
|
68
|
+
|
69
|
+
expected_xml = Nokogiri::XML(expected, nil, 'UTF-8')
|
70
|
+
expect(xml.at_css('MNT_ATTRIBUTES')).to be_eq_node(expected_xml.at_css('MNT_ATTRIBUTES'))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'response code' do
|
75
|
+
context 'when invalid status' do
|
76
|
+
let(:state) { 'foobar' }
|
77
|
+
it 'should raise error' do
|
78
|
+
expect { xml }.to raise_error(Payanyway::Response::InvalidState)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when set amount' do
|
83
|
+
let(:request_amount) { nil }
|
84
|
+
let(:amount) { 10.10 }
|
85
|
+
|
86
|
+
it 'should eq 100' do
|
87
|
+
code_node = xml.at_css('MNT_RESULT_CODE')
|
88
|
+
expect(code_node.text).to eq('100')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when valid status' do
|
93
|
+
let(:state) { :paid }
|
94
|
+
|
95
|
+
it 'should eq 200' do
|
96
|
+
code_node = xml.at_css('MNT_RESULT_CODE')
|
97
|
+
expect(code_node.text).to eq('200')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -12,20 +12,9 @@ describe Payanyway::Response::Pay do
|
|
12
12
|
}
|
13
13
|
end
|
14
14
|
|
15
|
-
describe '#
|
16
|
-
subject { service.pretty_params }
|
17
|
-
|
18
|
-
its([:moneta_id]) { is_expected.to eq(1) }
|
19
|
-
its([:amount]) { is_expected.to eq(10.20) }
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '#perform' do
|
15
|
+
describe '#success?' do
|
23
16
|
subject { service.success? }
|
24
17
|
|
25
|
-
|
26
|
-
before { service.perform }
|
27
|
-
|
28
|
-
it { is_expected.to be_truthy }
|
29
|
-
end
|
18
|
+
it { is_expected.to be_truthy }
|
30
19
|
end
|
31
|
-
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: payanyway
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ssnikolay
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 3.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,10 +170,11 @@ files:
|
|
156
170
|
- lib/payanyway/controller.rb
|
157
171
|
- lib/payanyway/engine.rb
|
158
172
|
- lib/payanyway/gateway.rb
|
159
|
-
- lib/payanyway/helpers/payment_url.rb
|
160
173
|
- lib/payanyway/helpers/settings.rb
|
161
174
|
- lib/payanyway/helpers/signature_generator.rb
|
175
|
+
- lib/payanyway/request/payment_url.rb
|
162
176
|
- lib/payanyway/response/base.rb
|
177
|
+
- lib/payanyway/response/check.rb
|
163
178
|
- lib/payanyway/response/pay.rb
|
164
179
|
- lib/payanyway/version.rb
|
165
180
|
- payanyway.gemspec
|
@@ -170,6 +185,9 @@ files:
|
|
170
185
|
- spec/internal/config/payanyway.yml
|
171
186
|
- spec/lib/payanyway/gateway_spec.rb
|
172
187
|
- spec/lib/payanyway/helpers/signature_generator_spec.rb
|
188
|
+
- spec/lib/payanyway/request/payment_url_spec.rb
|
189
|
+
- spec/lib/payanyway/response/base_spec.rb
|
190
|
+
- spec/lib/payanyway/response/check_spec.rb
|
173
191
|
- spec/lib/payanyway/response/pay_spec.rb
|
174
192
|
- spec/spec_helper.rb
|
175
193
|
homepage: ''
|
@@ -204,5 +222,8 @@ test_files:
|
|
204
222
|
- spec/internal/config/payanyway.yml
|
205
223
|
- spec/lib/payanyway/gateway_spec.rb
|
206
224
|
- spec/lib/payanyway/helpers/signature_generator_spec.rb
|
225
|
+
- spec/lib/payanyway/request/payment_url_spec.rb
|
226
|
+
- spec/lib/payanyway/response/base_spec.rb
|
227
|
+
- spec/lib/payanyway/response/check_spec.rb
|
207
228
|
- spec/lib/payanyway/response/pay_spec.rb
|
208
229
|
- spec/spec_helper.rb
|