walletone 0.1.2 → 0.3.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 +7 -0
- data/lib/walletone.rb +3 -0
- data/lib/walletone/error_response.rb +16 -0
- data/lib/walletone/fields.rb +5 -1
- data/lib/walletone/form.rb +8 -1
- data/lib/walletone/invoicing.rb +5 -0
- data/lib/walletone/invoicing/client.rb +103 -0
- data/lib/walletone/notification.rb +28 -13
- data/lib/walletone/payment.rb +2 -3
- data/lib/walletone/recurrent.rb +8 -0
- data/lib/walletone/recurrent/client.rb +76 -0
- data/lib/walletone/recurrent/invoice.rb +15 -0
- data/lib/walletone/recurrent/payment.rb +7 -0
- data/lib/walletone/recurrent/result_invoice.rb +66 -0
- data/lib/walletone/signer.rb +8 -4
- data/lib/walletone/version.rb +1 -1
- data/spec/spec_helper.rb +4 -0
- data/spec/walletone/invoicing/client_spec.rb +21 -0
- data/spec/walletone/middleware/base_spec.rb +1 -1
- data/spec/walletone/middleware/callback_spec.rb +1 -1
- data/spec/walletone/notification_spec.rb +26 -0
- data/spec/walletone/recurrent/client_spec.rb +63 -0
- data/spec/walletone/recurrent/payment_spec.rb +21 -0
- data/spec/walletone_spec.rb +9 -2
- data/walletone.gemspec +6 -1
- metadata +95 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b0bdb40bad763d10f9d0b928d801527887dea09
|
4
|
+
data.tar.gz: bd4f7256beceb5f27bd5867ef4989c009e0016cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5858ba537300604f708e9fa3f8337731897335abf55f5cce5fe2e337a9f221bbbe3ba048495bc2ee861090411aba7042c19aa9ff72f908d64bd3ac3726c3b5f5
|
7
|
+
data.tar.gz: 25e8bcb648e34a2c71f89ea9cea5f2ef1bc9bd095f5297ff754b5b2cf70c38018362aae2d6e0b3773ca853d9d4ccf9d0d8fe37f3b8c706b2fbcfb79c93c8b7a5
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/BrandyMint/walletone)
|
4
4
|
[](https://codeclimate.com/github/BrandyMint/walletone)
|
5
|
+
[](https://codeclimate.com/github/BrandyMint/walletone)
|
5
7
|
|
6
8
|
---
|
7
9
|
|
@@ -116,6 +118,10 @@ form = payment.form
|
|
116
118
|
|
117
119
|
1. [Walletone Open API](https://api.w1.ru/OpenApi/)
|
118
120
|
2. [Документация (API)](http://www.walletone.com/ru/merchant/documentation/)
|
121
|
+
3. [Доставка redexpress](http://www.walletone.com/en/merchant/delivery/about/)
|
122
|
+
4. [Рекуренты](https://docs.google.com/document/d/1_1HponT9Xv5dJ10Lqh23JyNGhqhNq9myb4xXl-gUOCI/pub?embedded=true)
|
123
|
+
5.
|
124
|
+
[InvoicingAPI](https://docs.google.com/document/d/18YaYbAwHo5jKCx88ox3okj1RJqcPF_2gSDdzpK2XHDI/pub?embedded=true)
|
119
125
|
|
120
126
|
Sponsored by http://taaasty.com and http://kiiiosk.ru
|
121
127
|
Developers are http://brandymint.com
|
@@ -129,3 +135,4 @@ Developers are http://brandymint.com
|
|
129
135
|
3. Commit your changes (`git commit -am 'add some feature'`)
|
130
136
|
4. Push to the branch (`git push origin my-new-feature`)
|
131
137
|
5. Create new Pull Request
|
138
|
+
|
data/lib/walletone.rb
CHANGED
@@ -5,6 +5,9 @@ require 'walletone/payment'
|
|
5
5
|
require 'walletone/notification'
|
6
6
|
require 'walletone/form'
|
7
7
|
require 'walletone/middleware/base'
|
8
|
+
require 'walletone/recurrent'
|
9
|
+
require 'walletone/invoicing'
|
10
|
+
require 'walletone/error_response'
|
8
11
|
|
9
12
|
module Walletone
|
10
13
|
API_URL = 'https://api.w1.ru/OpenApi/'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Walletone
|
2
|
+
class ErrorResponse < StandardError
|
3
|
+
def initialize(response)
|
4
|
+
fail 'Must be a Net::HTTPInternalServerError' unless response.is_a? Net::HTTPInternalServerError
|
5
|
+
@response = response
|
6
|
+
end
|
7
|
+
|
8
|
+
def message
|
9
|
+
"[#{response.code}] #{response.body}"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :response
|
15
|
+
end
|
16
|
+
end
|
data/lib/walletone/fields.rb
CHANGED
@@ -7,7 +7,7 @@ module Walletone
|
|
7
7
|
# > payment.WMI_MERCHANT_ID
|
8
8
|
# > payment.WMI_MERCHANT_ID=123
|
9
9
|
#
|
10
|
-
# > payment.WMI_PTENABLED =
|
10
|
+
# > payment.WMI_PTENABLED =
|
11
11
|
fields.each do |k|
|
12
12
|
define_method k do
|
13
13
|
fetch k
|
@@ -48,6 +48,10 @@ module Walletone
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
def to_s
|
52
|
+
as_list.to_s
|
53
|
+
end
|
54
|
+
|
51
55
|
private
|
52
56
|
|
53
57
|
def signer
|
data/lib/walletone/form.rb
CHANGED
@@ -11,6 +11,14 @@ module Walletone
|
|
11
11
|
@payment = payment
|
12
12
|
end
|
13
13
|
|
14
|
+
def to_s
|
15
|
+
payment.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def as_list
|
19
|
+
payment.as_list
|
20
|
+
end
|
21
|
+
|
14
22
|
def checkout_url
|
15
23
|
Walletone.config.web_checkout_url
|
16
24
|
end
|
@@ -32,6 +40,5 @@ module Walletone
|
|
32
40
|
def hidden_field_tag name, value
|
33
41
|
"<input name=\"#{CGI.escapeHTML(name.to_s)}\" type=\"hidden\" value=\"#{CGI.escapeHTML(value.to_s)}\" />"
|
34
42
|
end
|
35
|
-
|
36
43
|
end
|
37
44
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'money'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
# Invoicing: https://docs.google.com/document/d/18YaYbAwHo5jKCx88ox3okj1RJqcPF_2gSDdzpK2XHDI/pub?embedded=true
|
5
|
+
# Recurrent: https://docs.google.com/document/d/1_1HponT9Xv5dJ10Lqh23JyNGhqhNq9myb4xXl-gUOCI/pub?embedded=true
|
6
|
+
#
|
7
|
+
module Walletone::Invoicing
|
8
|
+
class Client
|
9
|
+
CABINET_ID = 'checkout' # yessplaycheckout
|
10
|
+
OPERATORS = %w(MtsRUB MegafonRUB Tele2RUB BeelineRUB)
|
11
|
+
|
12
|
+
BASE_URL = "https://wl.walletone.com/#{CABINET_ID}/invoicingapi/"
|
13
|
+
|
14
|
+
def initialize(user_id:, secret_key:, hash_type: Walletone::Signer::DEFAULT_HASH_TYPE)
|
15
|
+
@user_id = user_id or fail 'no user_id'
|
16
|
+
@user_id = @user_id.to_s
|
17
|
+
@secret_key = secret_key or fail 'no secret_key'
|
18
|
+
@hash_type = hash_type or fail 'no hash_type'
|
19
|
+
@timestamp = Time.now.utc.strftime('%FT%T')
|
20
|
+
end
|
21
|
+
|
22
|
+
def do_phone_payment(amount:, order_id:, phone: ,additional_params:)
|
23
|
+
invoice = make_invoice amount: amount, order_id: order_id, additional_params: additional_params
|
24
|
+
make_payments_process_with_phone payment_id: invoice['Invoice']['Payment']['PaymentId'], phone: phone
|
25
|
+
end
|
26
|
+
|
27
|
+
# Для рекурентов
|
28
|
+
# {
|
29
|
+
# "CustomerId":"123456",
|
30
|
+
# "UseSavedAuthData":true,
|
31
|
+
# "CreditCardTerminal":Non3Ds,
|
32
|
+
# "AuthData": [
|
33
|
+
# "RecurrentCreditCardAuthSecurityCode":"123"
|
34
|
+
# ]
|
35
|
+
# }
|
36
|
+
|
37
|
+
def make_payments_process(payment_id, params = {})
|
38
|
+
fail 'must be payment_id' unless payment_id
|
39
|
+
make_request "payments/#{payment_id}/process", params.to_json
|
40
|
+
end
|
41
|
+
|
42
|
+
def make_payments_process_with_phone(payment_id:, customer_id:, phone:)
|
43
|
+
make_payments_process payment_id, { 'CustomerId' => customer_id, 'AuthData' => { 'MobileCommercePhoneNumber' => phone } }
|
44
|
+
end
|
45
|
+
|
46
|
+
def make_invoice(invoice)
|
47
|
+
fail 'Must be a RecurrentInvoice' unless invoice.is_a? Walletone::Recurrent::Invoice
|
48
|
+
|
49
|
+
body = {
|
50
|
+
'OrderId' => invoice.OrderId.to_s,
|
51
|
+
'Amount' => invoice.Amount.to_f,
|
52
|
+
'CurrencyId' => invoice.CurrencyId,
|
53
|
+
'PaymentTypeId' => invoice.PaymentTypeId
|
54
|
+
}
|
55
|
+
|
56
|
+
body['InvoiceAdditionalParams'] = invoice.InvoiceAdditionalParams if invoice.InvoiceAdditionalParams.is_a?(Hash) && !invoice.InvoiceAdditionalParams.empty?
|
57
|
+
|
58
|
+
make_request 'invoices', body.to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
attr_reader :user_id, :secret_key, :timestamp, :hash_type
|
64
|
+
|
65
|
+
def make_request(path, body)
|
66
|
+
uri = URI.parse BASE_URL + path
|
67
|
+
|
68
|
+
headers = build_headers(uri.to_s, body)
|
69
|
+
|
70
|
+
req = Net::HTTP::Post.new uri, headers
|
71
|
+
req.body = body
|
72
|
+
|
73
|
+
log "Request [#{uri}]: #{body}"
|
74
|
+
http = build_http uri
|
75
|
+
response = http.request req
|
76
|
+
log "Response [#{uri}] #{body}: [#{response.code}] #{response.body}"
|
77
|
+
fail Walletone::ErrorResponse, response if response.is_a? Net::HTTPInternalServerError
|
78
|
+
|
79
|
+
MultiJson.load response.body
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_http(uri)
|
83
|
+
Net::HTTP.start uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE # OpenSSL::SSL::VERIFY_PEER
|
84
|
+
end
|
85
|
+
|
86
|
+
def signature url, body
|
87
|
+
Walletone::Signer.sign [url, user_id, timestamp, body, secret_key].join, hash_type
|
88
|
+
end
|
89
|
+
|
90
|
+
def log(message)
|
91
|
+
Walletone.logger.info "InvoicingAPI: #{message}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_headers(url, body)
|
95
|
+
{
|
96
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
97
|
+
'X-Wallet-Timestamp' => timestamp,
|
98
|
+
'X-Wallet-UserId' => user_id,
|
99
|
+
'X-Wallet-Signature' => signature(url, body)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -6,19 +6,34 @@ require 'walletone/fields'
|
|
6
6
|
module Walletone
|
7
7
|
class Notification < Fields
|
8
8
|
FIELDS = %i(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
9
|
+
WMI_AUTO_ACCEPT
|
10
|
+
|
11
|
+
WMI_NOTIFY_COUNT
|
12
|
+
WMI_LAST_NOTIFY_DATE
|
13
|
+
|
14
|
+
WMI_MERCHANT_ID
|
15
|
+
|
16
|
+
WMI_RECURRING_PAYMENTS_APPROVED
|
17
|
+
|
18
|
+
WMI_PAYMENT_NO
|
19
|
+
WMI_PAYMENT_AMOUNT
|
20
|
+
WMI_PAYMENT_TYPE
|
21
|
+
|
22
|
+
WMI_COMMISSION_AMOUNT
|
23
|
+
WMI_CURRENCY_ID
|
24
|
+
WMI_TO_USER_ID
|
25
|
+
|
26
|
+
WMI_ORDER_ID
|
27
|
+
WMI_ORDER_STATE
|
28
|
+
|
29
|
+
WMI_DESCRIPTION
|
30
|
+
WMI_SUCCESS_URL WMI_FAIL_URL
|
31
|
+
WMI_EXPIRED_DATE
|
32
|
+
WMI_CREATE_DATE WMI_UPDATE_DATE
|
33
|
+
|
34
|
+
WMI_RECURRENCE_STATE_ID
|
35
|
+
|
36
|
+
WMI_SIGNATURE
|
22
37
|
)
|
23
38
|
|
24
39
|
define_fields FIELDS
|
data/lib/walletone/payment.rb
CHANGED
@@ -28,9 +28,9 @@ module Walletone
|
|
28
28
|
WMI_CUSTOMER_FIRSTNAME WMI_CUSTOMER_LASTNAME WMI_CUSTOMER_EMAIL
|
29
29
|
WMI_CULTURE_ID
|
30
30
|
WMI_SIGNATURE
|
31
|
-
WMI_DELIVERY_REQUEST
|
31
|
+
WMI_DELIVERY_REQUEST
|
32
32
|
WMI_DELIVERY_COUNTRY WMI_DELIVERY_REGION WMI_DELIVERY_CITY WMI_DELIVERY_ADDRESS
|
33
|
-
WMI_DELIVERY_CONTACTINFO WMI_DELIVERY_COMMENTS WMI_DELIVERY_ORDERID
|
33
|
+
WMI_DELIVERY_CONTACTINFO WMI_DELIVERY_COMMENTS WMI_DELIVERY_ORDERID
|
34
34
|
WMI_DELIVERY_DATEFROM WMI_DELIVERY_DATETILL
|
35
35
|
WMI_PSP_MERCHANT_ID
|
36
36
|
)
|
@@ -87,6 +87,5 @@ module Walletone
|
|
87
87
|
value = fetch :WMI_EXPIRED_DATE
|
88
88
|
value ? value.iso8601 : value
|
89
89
|
end
|
90
|
-
|
91
90
|
end
|
92
91
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Walletone::Recurrent
|
4
|
+
require 'walletone/recurrent/invoice'
|
5
|
+
require 'walletone/recurrent/result_invoice'
|
6
|
+
|
7
|
+
class Client
|
8
|
+
RECURRENT_API = 'https://wl.walletone.com/checkout/invoicingapi/'
|
9
|
+
INVOICE_CLASS = Walletone::Recurrent::Invoice
|
10
|
+
RESULT_INVOICE_CLASS = Walletone::Recurrent::ResultInvoice
|
11
|
+
|
12
|
+
# @param [merchant_id]
|
13
|
+
#
|
14
|
+
def initialize(merchant_id:, customer_id:, secret_key:, hash_type: Walletone::Signer::DEFAULT_HASH_TYPE)
|
15
|
+
@merchant_id = merchant_id.to_s
|
16
|
+
@customer_id = customer_id.to_s
|
17
|
+
@secret_key = secret_key.to_s
|
18
|
+
@hash_type = hash_type
|
19
|
+
@timestamp = Time.now.utc.strftime('%FT%T')
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_invoice(invoice)
|
23
|
+
fail "Must be a #{INVOICE_CLASS} (#{invoice})" unless invoice.is_a? INVOICE_CLASS
|
24
|
+
|
25
|
+
result = do_request RECURRENT_API + 'invoices', invoice.to_json
|
26
|
+
|
27
|
+
RESULT_INVOICE_CLASS.new result
|
28
|
+
end
|
29
|
+
|
30
|
+
def make_payment(payment_id: nil, email: nil)
|
31
|
+
body = { CustomerId: customer_id, CreditCardTerminal: 'Non3Ds', UseSavedAuthData: 'true', AuthData: { RecurrentCreditCardEmail: email } }.to_json
|
32
|
+
|
33
|
+
do_request RECURRENT_API + "payments/#{payment_id}/process", body
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :merchant_id, :customer_id, :secret_key, :hash_type, :timestamp, :hash_type
|
39
|
+
|
40
|
+
def do_request url, body
|
41
|
+
uri = URI.parse url
|
42
|
+
|
43
|
+
req = Net::HTTP::Post.new uri, build_headers(url, body)
|
44
|
+
req.body = body
|
45
|
+
|
46
|
+
log "Request [#{uri}]: #{body}"
|
47
|
+
http = build_http uri
|
48
|
+
response = http.request req
|
49
|
+
log "Response [#{uri}] #{body}:\n [#{response.code}] #{response.body}"
|
50
|
+
fail Walletone::ErrorResponse, response if response.is_a? Net::HTTPInternalServerError
|
51
|
+
|
52
|
+
MultiJson.load response.body
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_http(uri)
|
56
|
+
Net::HTTP.start uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE # OpenSSL::SSL::VERIFY_PEER
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_headers(url, body)
|
60
|
+
{
|
61
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
62
|
+
'X-Wallet-UserId' => merchant_id,
|
63
|
+
'X-Wallet-Timestamp' => timestamp,
|
64
|
+
'X-Wallet-Signature' => signature(url, body)
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def log(message)
|
69
|
+
Walletone.logger.info "Recurrent::Client: #{message}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def signature url, body
|
73
|
+
Walletone::Signer.sign [url, merchant_id, timestamp, body, secret_key].join, hash_type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Walletone::Recurrent
|
2
|
+
class Invoice
|
3
|
+
include Virtus.model strict: true, coerce: false
|
4
|
+
|
5
|
+
attribute :OrderId, String, required: true
|
6
|
+
attribute :Amount, Float, required: true
|
7
|
+
attribute :CurrencyId, Integer, required: true, default: 643 # rub
|
8
|
+
attribute :PaymentTypeId, String, required: true, default: 'CreditCardRUB' # MtsRUB, MegafonRUB, Tele2RUB, BeelineRUB
|
9
|
+
attribute :InvoiceAdditionalParams, Hash
|
10
|
+
|
11
|
+
def to_json
|
12
|
+
to_h.to_json
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Walletone::Recurrent
|
2
|
+
class ResultInvoice
|
3
|
+
class InvoiceInfo
|
4
|
+
RAW_EXAMPLE =
|
5
|
+
{"Invoice"=>
|
6
|
+
{"InvoiceId"=>345539868062,
|
7
|
+
"Amount"=>1.24,
|
8
|
+
"CurrencyId"=>643,
|
9
|
+
"InvoiceStateId"=>"Created",
|
10
|
+
"CreateDate"=>"2015-12-31T08:20:13",
|
11
|
+
"UpdateDate"=>"2015-12-31T08:20:13",
|
12
|
+
"Payment"=>
|
13
|
+
{"PaymentId"=>69904496,
|
14
|
+
"PaymentCode"=>"345539868062",
|
15
|
+
"PaymentCodeType"=>"InvoiceId",
|
16
|
+
"CreateDate"=>"2015-12-31T08:20:14.137",
|
17
|
+
"UpdateDate"=>"2015-12-31T08:20:14.137",
|
18
|
+
"PaymentStateId"=>"Created",
|
19
|
+
"PaymentTypeId"=>"CreditCardRUB"}},
|
20
|
+
"CanSaveAsExternalAccount"=>true}
|
21
|
+
|
22
|
+
class PaymentInfo
|
23
|
+
include Virtus.model strict: true
|
24
|
+
|
25
|
+
attribute :PaymentId, Integer
|
26
|
+
attribute :PaymentCode, String
|
27
|
+
attribute :PaymentCodeType, String # 'InvoiceId'
|
28
|
+
attribute :CreateDate, Time
|
29
|
+
attribute :UpdateDate, Time
|
30
|
+
attribute :PaymentStateId, String # 'Created'
|
31
|
+
# Состояние платежа в ответе на запрос подтверждения списания средств. Может принимать одно из следующих состояний:
|
32
|
+
# Paid — списание произошло успешно.
|
33
|
+
# Error — произошла ошибка
|
34
|
+
# Created — создан, оплата не поступала
|
35
|
+
# Processing — в обработке
|
36
|
+
attribute :PaymentTypeId, String # 'CreditCardRUB'
|
37
|
+
end
|
38
|
+
|
39
|
+
include Virtus.model strict: true
|
40
|
+
|
41
|
+
attribute :InvoiceId, Integer
|
42
|
+
attribute :Amount, Float
|
43
|
+
attribute :InvoiceStateId, String # 'Created'
|
44
|
+
# Параметр “InvoiceStateId”
|
45
|
+
# Сосотяние выставленного счета в ответе на создание счета. Может принимать одно из следующих значений:
|
46
|
+
# Created — счет успешно создан.
|
47
|
+
# Canceled — отменен
|
48
|
+
# Accepted — оплачен
|
49
|
+
# Expired — вышел срок действия
|
50
|
+
# Processing — в процессе подтверждения
|
51
|
+
# Received — получен, ожидает принятия
|
52
|
+
# Rejected — отвергнут
|
53
|
+
# PartiallyPaid — частично оплачен
|
54
|
+
# OverPaid — переплачен
|
55
|
+
attribute :CreateDate, Time
|
56
|
+
attribute :UpdateDate, Time
|
57
|
+
attribute :Payment, PaymentInfo
|
58
|
+
end
|
59
|
+
|
60
|
+
include Virtus.model strict: true
|
61
|
+
|
62
|
+
attribute :Invoice, InvoiceInfo, required: true
|
63
|
+
attribute :CanSaveAsExternalAccount, Boolean
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/lib/walletone/signer.rb
CHANGED
@@ -12,17 +12,21 @@ module Walletone
|
|
12
12
|
|
13
13
|
attribute :fields, Walletone::Fields, requried: true
|
14
14
|
|
15
|
-
def
|
15
|
+
def self.sign(content, hash_type)
|
16
16
|
case hash_type
|
17
17
|
when :md5
|
18
|
-
Digest::MD5.base64digest
|
18
|
+
Digest::MD5.base64digest content
|
19
19
|
when :sha1
|
20
|
-
Digest::SHA1.base64digest
|
20
|
+
Digest::SHA1.base64digest content
|
21
21
|
else
|
22
22
|
raise ArgumentError, hash_type
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
def signature secret_key, hash_type=DEFAULT_HASH_TYPE
|
27
|
+
self.class.sign fields_as_string( secret_key ), hash_type
|
28
|
+
end
|
29
|
+
|
26
30
|
private
|
27
31
|
|
28
32
|
def fields_as_string secret_key
|
@@ -34,7 +38,7 @@ module Walletone
|
|
34
38
|
def sorted_values
|
35
39
|
fields
|
36
40
|
.as_list
|
37
|
-
.reject { |
|
41
|
+
.reject { |f| f.first == WMI_SIGNATURE }
|
38
42
|
.sort { |a, b| [ a[0], a[1] ] <=> [ b[0], b[1] ] }
|
39
43
|
.map(&:last)
|
40
44
|
.map(&:to_s)
|
data/lib/walletone/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'walletone/invoicing'
|
2
|
+
|
3
|
+
describe Walletone::Invoicing::Client do
|
4
|
+
let(:user_id) { 127830694600 }
|
5
|
+
let(:secret_key) { 'abc' }
|
6
|
+
let(:amount) { Money.new 1000, 'rub' }
|
7
|
+
let(:order_id) { '123' }
|
8
|
+
|
9
|
+
let(:success_response) { '{"Invoice":{"InvoiceId":123,"Amount":10.0000,"CurrencyId":643,"InvoiceStateId":"Created","CreateDate":"2015-11-02T08:11:57","UpdateDate":"2015-11-02T08:11:57","Payment":{"PaymentId":123,"Amount":10.0000,"CurrencyId":643,"PaymentCode":"123","PaymentCodeType":"InvoiceId","CreateDate":"2015-11-02T08:11:57","UpdateDate":"2015-11-02T08:11:57","PaymentStateId":"Created","PaymentTypeId":"MtsRUB","ExternalAccountId":null}},"CanSaveAsExternalAccount":false}' }
|
10
|
+
|
11
|
+
subject { Walletone::Invoicing::Client.new user_id: user_id, secret_key: secret_key }
|
12
|
+
|
13
|
+
let(:invoice) { Walletone::Recurrent::Invoice.new Amount: amount, OrderId: order_id }
|
14
|
+
|
15
|
+
context '#make_invoice' do
|
16
|
+
it do
|
17
|
+
stub_request(:post, Walletone::Invoicing::Client::BASE_URL + 'invoices').to_return(body: success_response)
|
18
|
+
expect(subject.make_invoice(invoice)).to be_a(Hash)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -4,7 +4,7 @@ require 'walletone/middleware/base'
|
|
4
4
|
describe Walletone::Middleware::Base do
|
5
5
|
let(:middleware) { described_class.new }
|
6
6
|
let!(:params) { { cp1251: 'привет'.encode('windows-1251') } }
|
7
|
-
subject
|
7
|
+
subject { Rack::MockRequest.new(middleware).post('/', params: params ) }
|
8
8
|
|
9
9
|
it do
|
10
10
|
expect(subject.status).to eq 200
|
@@ -5,7 +5,7 @@ describe Walletone::Middleware::Callback do
|
|
5
5
|
let(:callback) { -> (notify, env) { } }
|
6
6
|
let(:middleware) { described_class.new callback }
|
7
7
|
let!(:params) { { cp1251: 'привет'.encode('windows-1251') } }
|
8
|
-
subject
|
8
|
+
subject { Rack::MockRequest.new(middleware).post('/', params: params ) }
|
9
9
|
|
10
10
|
it do
|
11
11
|
expect(subject.body).to eq 'WMI_RESULT=OK'
|
@@ -39,4 +39,30 @@ describe Walletone::Notification do
|
|
39
39
|
expect( subject).to be_accepted
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
describe 'recurrent' do
|
44
|
+
let(:raw_params) do
|
45
|
+
{
|
46
|
+
"WMI_AUTO_ACCEPT": "1",
|
47
|
+
"WMI_COMMISSION_AMOUNT": "0.04",
|
48
|
+
"WMI_CREATE_DATE": "2015-11-08 06:04:05",
|
49
|
+
"WMI_CURRENCY_ID": "643",
|
50
|
+
"WMI_EXPIRED_DATE": "2015-12-08 06:04:05",
|
51
|
+
"WMI_EXTERNAL_ACCOUNT_ID": "521324******2146",
|
52
|
+
"WMI_FAIL_URL": "http://kiosk.3001.vkontraste.ru/payments/w1/failure",
|
53
|
+
"WMI_LAST_NOTIFY_DATE": "2015-11-08 11:17:49",
|
54
|
+
"WMI_MERCHANT_ID": "120464954417",
|
55
|
+
"WMI_NOTIFY_COUNT": "29",
|
56
|
+
"WMI_ORDER_ID": "345825935381",
|
57
|
+
"WMI_ORDER_STATE": "Accepted",
|
58
|
+
"WMI_PAYMENT_AMOUNT": "1.00",
|
59
|
+
"WMI_PAYMENT_NO": "merchant-68-invoice-1",
|
60
|
+
"WMI_PAYMENT_TYPE": "CreditCardRUB",
|
61
|
+
"WMI_RECURRING_PAYMENTS_APPROVED": "1",
|
62
|
+
"WMI_SUCCESS_URL": "http://kiosk.3001.vkontraste.ru/payments/w1/success",
|
63
|
+
"WMI_UPDATE_DATE": "2015-11-08 06:18:33",
|
64
|
+
"WMI_SIGNATURE": signature
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
42
68
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'walletone/recurrent'
|
2
|
+
require 'walletone/recurrent/invoice'
|
3
|
+
require 'walletone/recurrent/client'
|
4
|
+
|
5
|
+
describe Walletone::Recurrent::Client do
|
6
|
+
let(:merchant_id) { 127830694690 }
|
7
|
+
let(:secret_key) { '3475706857624f46344e573753316e387c396e5f4b54767b796c4c' }
|
8
|
+
let(:customer_id) { 123 }
|
9
|
+
|
10
|
+
let(:client) { Walletone::Recurrent::Client.new merchant_id: merchant_id, secret_key: secret_key, customer_id: customer_id }
|
11
|
+
|
12
|
+
let(:fields) do
|
13
|
+
Walletone::Fields.new WMI_PAYMENT_AMOUNT: 123, WMI_MERCHANT_ID: merchant_id, WMI_CUSTOMER_ID: 123, WMI_RECURRING_AGREEMENT_URL: 'http://'
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:invoice) { Walletone::Recurrent::Invoice.new Amount: 123, OrderId: 456 }
|
17
|
+
|
18
|
+
context '#create_invoice' do
|
19
|
+
let(:result) {
|
20
|
+
{"Invoice"=>
|
21
|
+
{"InvoiceId"=>345539868062,
|
22
|
+
"Amount"=>1.24,
|
23
|
+
"CurrencyId"=>643,
|
24
|
+
"InvoiceStateId"=>"Created",
|
25
|
+
"CreateDate"=>"2015-12-31T08:20:13",
|
26
|
+
"UpdateDate"=>"2015-12-31T08:20:13",
|
27
|
+
"Payment"=>
|
28
|
+
{"PaymentId"=>69904496,
|
29
|
+
"PaymentCode"=>"345539868062",
|
30
|
+
"PaymentCodeType"=>"InvoiceId",
|
31
|
+
"CreateDate"=>"2015-12-31T08:20:14.137",
|
32
|
+
"UpdateDate"=>"2015-12-31T08:20:14.137",
|
33
|
+
"PaymentStateId"=>"Created",
|
34
|
+
"PaymentTypeId"=>"CreditCardRUB"}},
|
35
|
+
"CanSaveAsExternalAccount"=>true}
|
36
|
+
}
|
37
|
+
before do
|
38
|
+
stub_request(:post, "https://wl.walletone.com/checkout/invoicingapi/invoices").
|
39
|
+
with(:body => "{\"OrderId\":456,\"Amount\":123,\"CurrencyId\":643,\"PaymentTypeId\":\"CreditCardRUB\",\"InvoiceAdditionalParams\":{}}").
|
40
|
+
to_return(:status => 200, :body => result.to_json, :headers => {'Content-Type'=>'application/json; charset=utf-8'})
|
41
|
+
end
|
42
|
+
it do
|
43
|
+
res = client.create_invoice invoice
|
44
|
+
expect(res).to be_a Walletone::Recurrent::ResultInvoice
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context '#make_payment' do
|
49
|
+
let(:payment_id) { 123 }
|
50
|
+
let(:result) { {} }
|
51
|
+
before do
|
52
|
+
stub_request(:post, "https://wl.walletone.com/checkout/invoicingapi/payments/123/process").
|
53
|
+
with(:body => "{\"CustomerId\":\"123\",\"CreditCardTerminal\":\"Non3Ds\",\"UseSavedAuthData\":\"true\",\"AuthData\":{\"RecurrentCreditCardEmail\":\"some@email.ru\"}}").
|
54
|
+
to_return(:status => 200, :body => result.to_json, :headers => {'Content-Type'=>'application/json; charset=utf-8'})
|
55
|
+
|
56
|
+
end
|
57
|
+
pending 'ответ'
|
58
|
+
#it do
|
59
|
+
#res = client.make_payment payment_id: payment_id, email: 'some@email.ru'
|
60
|
+
#expect(res).to eq 'aaa'
|
61
|
+
#end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'walletone/recurrent'
|
2
|
+
require 'walletone/recurrent/payment'
|
3
|
+
|
4
|
+
describe Walletone::Recurrent::Payment do
|
5
|
+
let(:merchant_id) { 127830694690 }
|
6
|
+
let(:secret_key) { '3475706857624f46344e573753316e387c396e5f4b54767b796c4c' }
|
7
|
+
|
8
|
+
subject { described_class.new( fields ) }
|
9
|
+
|
10
|
+
let(:fields) do
|
11
|
+
Walletone::Fields.new WMI_PAYMENT_AMOUNT: 123, WMI_MERCHANT_ID: merchant_id, WMI_CUSTOMER_ID: 123, WMI_RECURRING_AGREEMENT_URL: 'http://'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'works' do
|
15
|
+
expect(subject.WMI_MERCHANT_ID).to eq(merchant_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'valid' do
|
19
|
+
expect(subject).to be_valid
|
20
|
+
end
|
21
|
+
end
|
data/spec/walletone_spec.rb
CHANGED
@@ -5,19 +5,26 @@ describe Walletone do
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
#let(:notifier) { notifier = Class.new; def notifier.notify *args; end }
|
9
|
+
#let(:notifier) { double }
|
10
|
+
|
8
11
|
describe 'Использует Bugsnag если он есть' do
|
9
12
|
before do
|
10
|
-
::Bugsnag = double
|
11
13
|
# Сбрасываем конфигурацию, чтобы она еще раз создалась
|
12
14
|
Walletone::Configuration.instance_variable_set('@singleton__instance__',nil)
|
13
15
|
end
|
14
16
|
|
15
17
|
it 'должен найти и применить Bugsnag' do
|
18
|
+
module ::Bugsnag
|
19
|
+
def self.notify *args
|
20
|
+
end
|
21
|
+
end
|
16
22
|
expect(Walletone.config.error_notifier).to eq Bugsnag
|
17
23
|
end
|
18
24
|
|
19
25
|
after do
|
20
|
-
::
|
26
|
+
Walletone::Configuration.instance_variable_set('@singleton__instance__',nil)
|
27
|
+
Object.send(:remove_const, :Bugsnag)
|
21
28
|
end
|
22
29
|
end
|
23
30
|
|
data/walletone.gemspec
CHANGED
@@ -21,15 +21,20 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency 'virtus', '~> 1.0'
|
22
22
|
s.add_dependency 'rack', '~> 1.5'
|
23
23
|
|
24
|
+
s.add_runtime_dependency 'faraday'
|
25
|
+
s.add_runtime_dependency 'money'
|
26
|
+
|
27
|
+
s.add_development_dependency 'multi_json', '>= 1.3.2'
|
28
|
+
s.add_development_dependency 'webmock', '>= 1.22'
|
24
29
|
s.add_development_dependency 'rake', '>= 10.4.0'
|
25
30
|
s.add_development_dependency 'rspec', '>= 3.2.0'
|
26
31
|
s.add_development_dependency 'ffaker', '>= 2.0.0'
|
27
32
|
s.add_development_dependency 'fabrication', '>= 2.12'
|
28
33
|
s.add_development_dependency "pry", '~> 0'
|
29
34
|
s.add_development_dependency "pry-nav", '~> 0'
|
30
|
-
s.add_development_dependency "webmock", '>= 1.21'
|
31
35
|
s.add_development_dependency "guard", '>= 2.12'
|
32
36
|
s.add_development_dependency "guard-rspec", '>= 4.5.0'
|
33
37
|
s.add_development_dependency 'guard-ctags-bundler', '>= 1.4.0'
|
34
38
|
s.add_development_dependency 'yard', '~> 0'
|
39
|
+
s.add_development_dependency "codeclimate-test-reporter"
|
35
40
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: walletone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danil Pismenny
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-12-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: virtus
|
@@ -39,6 +39,62 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '1.5'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: faraday
|
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: money
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: multi_json
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.3.2
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.3.2
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: webmock
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '1.22'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '1.22'
|
42
98
|
- !ruby/object:Gem::Dependency
|
43
99
|
name: rake
|
44
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,20 +179,6 @@ dependencies:
|
|
123
179
|
- - "~>"
|
124
180
|
- !ruby/object:Gem::Version
|
125
181
|
version: '0'
|
126
|
-
- !ruby/object:Gem::Dependency
|
127
|
-
name: webmock
|
128
|
-
requirement: !ruby/object:Gem::Requirement
|
129
|
-
requirements:
|
130
|
-
- - ">="
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: '1.21'
|
133
|
-
type: :development
|
134
|
-
prerelease: false
|
135
|
-
version_requirements: !ruby/object:Gem::Requirement
|
136
|
-
requirements:
|
137
|
-
- - ">="
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
version: '1.21'
|
140
182
|
- !ruby/object:Gem::Dependency
|
141
183
|
name: guard
|
142
184
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,6 +235,20 @@ dependencies:
|
|
193
235
|
- - "~>"
|
194
236
|
- !ruby/object:Gem::Version
|
195
237
|
version: '0'
|
238
|
+
- !ruby/object:Gem::Dependency
|
239
|
+
name: codeclimate-test-reporter
|
240
|
+
requirement: !ruby/object:Gem::Requirement
|
241
|
+
requirements:
|
242
|
+
- - ">="
|
243
|
+
- !ruby/object:Gem::Version
|
244
|
+
version: '0'
|
245
|
+
type: :development
|
246
|
+
prerelease: false
|
247
|
+
version_requirements: !ruby/object:Gem::Requirement
|
248
|
+
requirements:
|
249
|
+
- - ">="
|
250
|
+
- !ruby/object:Gem::Version
|
251
|
+
version: '0'
|
196
252
|
description: "Клиент для приема оплаты через walletone.com"
|
197
253
|
email:
|
198
254
|
- danil@brandymint.ru
|
@@ -204,13 +260,21 @@ files:
|
|
204
260
|
- LICENSE
|
205
261
|
- README.md
|
206
262
|
- lib/walletone.rb
|
263
|
+
- lib/walletone/error_response.rb
|
207
264
|
- lib/walletone/fields.rb
|
208
265
|
- lib/walletone/form.rb
|
266
|
+
- lib/walletone/invoicing.rb
|
267
|
+
- lib/walletone/invoicing/client.rb
|
209
268
|
- lib/walletone/middleware.rb
|
210
269
|
- lib/walletone/middleware/base.rb
|
211
270
|
- lib/walletone/middleware/callback.rb
|
212
271
|
- lib/walletone/notification.rb
|
213
272
|
- lib/walletone/payment.rb
|
273
|
+
- lib/walletone/recurrent.rb
|
274
|
+
- lib/walletone/recurrent/client.rb
|
275
|
+
- lib/walletone/recurrent/invoice.rb
|
276
|
+
- lib/walletone/recurrent/payment.rb
|
277
|
+
- lib/walletone/recurrent/result_invoice.rb
|
214
278
|
- lib/walletone/signer.rb
|
215
279
|
- lib/walletone/version.rb
|
216
280
|
- spec/fabricators/fields_fabricator.rb
|
@@ -218,10 +282,13 @@ files:
|
|
218
282
|
- spec/spec_helper.rb
|
219
283
|
- spec/walletone/fields_spec.rb
|
220
284
|
- spec/walletone/form_spec.rb
|
285
|
+
- spec/walletone/invoicing/client_spec.rb
|
221
286
|
- spec/walletone/middleware/base_spec.rb
|
222
287
|
- spec/walletone/middleware/callback_spec.rb
|
223
288
|
- spec/walletone/notification_spec.rb
|
224
289
|
- spec/walletone/payment_spec.rb
|
290
|
+
- spec/walletone/recurrent/client_spec.rb
|
291
|
+
- spec/walletone/recurrent/payment_spec.rb
|
225
292
|
- spec/walletone/signer_spec.rb
|
226
293
|
- spec/walletone_spec.rb
|
227
294
|
- walletone.gemspec
|
@@ -245,20 +312,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
312
|
version: '0'
|
246
313
|
requirements: []
|
247
314
|
rubyforge_project:
|
248
|
-
rubygems_version: 2.
|
315
|
+
rubygems_version: 2.4.5
|
249
316
|
signing_key:
|
250
317
|
specification_version: 4
|
251
318
|
summary: walleton.com Checkout client
|
252
319
|
test_files:
|
253
|
-
- spec/
|
320
|
+
- spec/walletone_spec.rb
|
321
|
+
- spec/fabricators/payment_fabricator.rb
|
322
|
+
- spec/fabricators/fields_fabricator.rb
|
323
|
+
- spec/spec_helper.rb
|
324
|
+
- spec/walletone/signer_spec.rb
|
254
325
|
- spec/walletone/fields_spec.rb
|
326
|
+
- spec/walletone/payment_spec.rb
|
327
|
+
- spec/walletone/invoicing/client_spec.rb
|
255
328
|
- spec/walletone/form_spec.rb
|
256
|
-
- spec/walletone/notification_spec.rb
|
257
|
-
- spec/walletone/middleware/callback_spec.rb
|
258
329
|
- spec/walletone/middleware/base_spec.rb
|
259
|
-
- spec/walletone/
|
260
|
-
- spec/
|
261
|
-
- spec/
|
262
|
-
- spec/
|
263
|
-
- spec/walletone_spec.rb
|
330
|
+
- spec/walletone/middleware/callback_spec.rb
|
331
|
+
- spec/walletone/notification_spec.rb
|
332
|
+
- spec/walletone/recurrent/payment_spec.rb
|
333
|
+
- spec/walletone/recurrent/client_spec.rb
|
264
334
|
has_rdoc:
|