payanyway 1.0.0 → 1.2.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 +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
|