walletone 0.1.2 → 0.3.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 +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
|
[![Build Status](https://travis-ci.org/BrandyMint/walletone.svg)](https://travis-ci.org/BrandyMint/walletone)
|
4
4
|
[![Code Climate](https://codeclimate.com/github/BrandyMint/walletone/badges/gpa.svg)](https://codeclimate.com/github/BrandyMint/walletone)
|
5
|
+
[![Test
|
6
|
+
Coverage](https://codeclimate.com/github/BrandyMint/walletone/badges/coverage.svg)](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:
|