cloud_payments 0.0.3 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +12 -15
  3. data/Gemfile +2 -2
  4. data/README.md +74 -5
  5. data/Rakefile +1 -0
  6. data/cloud_payments.gemspec +7 -6
  7. data/config.ru +1 -0
  8. data/lib/cloud_payments.rb +1 -1
  9. data/lib/cloud_payments/client.rb +3 -2
  10. data/lib/cloud_payments/client/errors.rb +1 -0
  11. data/lib/cloud_payments/client/gateway_errors.rb +3 -1
  12. data/lib/cloud_payments/client/response.rb +2 -1
  13. data/lib/cloud_payments/client/serializer.rb +1 -0
  14. data/lib/cloud_payments/client/serializer/base.rb +9 -1
  15. data/lib/cloud_payments/client/serializer/multi_json.rb +1 -0
  16. data/lib/cloud_payments/config.rb +4 -0
  17. data/lib/cloud_payments/models.rb +2 -0
  18. data/lib/cloud_payments/models/like_subscription.rb +1 -0
  19. data/lib/cloud_payments/models/model.rb +1 -0
  20. data/lib/cloud_payments/models/on_fail.rb +2 -0
  21. data/lib/cloud_payments/models/on_kassa_receipt.rb +23 -0
  22. data/lib/cloud_payments/models/on_pay.rb +2 -0
  23. data/lib/cloud_payments/models/on_recurrent.rb +1 -0
  24. data/lib/cloud_payments/models/order.rb +1 -0
  25. data/lib/cloud_payments/models/secure3d.rb +1 -0
  26. data/lib/cloud_payments/models/subscription.rb +2 -1
  27. data/lib/cloud_payments/models/transaction.rb +9 -0
  28. data/lib/cloud_payments/namespaces.rb +12 -1
  29. data/lib/cloud_payments/namespaces/apple_pay.rb +18 -0
  30. data/lib/cloud_payments/namespaces/base.rb +2 -1
  31. data/lib/cloud_payments/namespaces/cards.rb +6 -0
  32. data/lib/cloud_payments/namespaces/kassa.rb +22 -0
  33. data/lib/cloud_payments/namespaces/orders.rb +5 -0
  34. data/lib/cloud_payments/namespaces/payments.rb +11 -0
  35. data/lib/cloud_payments/namespaces/subscriptions.rb +6 -0
  36. data/lib/cloud_payments/namespaces/tokens.rb +1 -0
  37. data/lib/cloud_payments/version.rb +2 -1
  38. data/lib/cloud_payments/webhooks.rb +7 -2
  39. data/spec/cloud_payments/client/response_spec.rb +3 -1
  40. data/spec/cloud_payments/client/serializer/multi_json_spec.rb +1 -0
  41. data/spec/cloud_payments/models/order_spec.rb +1 -0
  42. data/spec/cloud_payments/models/secure3d_spec.rb +1 -0
  43. data/spec/cloud_payments/models/subscription_spec.rb +1 -1
  44. data/spec/cloud_payments/models/transaction_spec.rb +25 -1
  45. data/spec/cloud_payments/namespaces/apple_pay_spec.rb +21 -0
  46. data/spec/cloud_payments/namespaces/base_spec.rb +10 -3
  47. data/spec/cloud_payments/namespaces/cards_spec.rb +42 -2
  48. data/spec/cloud_payments/namespaces/kassa_spec.rb +43 -0
  49. data/spec/cloud_payments/namespaces/orders_spec.rb +13 -0
  50. data/spec/cloud_payments/namespaces/payments_spec.rb +119 -0
  51. data/spec/cloud_payments/namespaces/subscriptions_spec.rb +21 -1
  52. data/spec/cloud_payments/namespaces/tokens_spec.rb +1 -0
  53. data/spec/cloud_payments/namespaces_spec.rb +2 -1
  54. data/spec/cloud_payments/webhooks_spec.rb +43 -0
  55. data/spec/cloud_payments_spec.rb +27 -4
  56. data/spec/fixtures/apis/cards/post3ds/failed.yml +45 -0
  57. data/spec/fixtures/apis/cards/post3ds/successful.yml +48 -0
  58. data/spec/fixtures/apis/orders/cancel/failed.yml +6 -0
  59. data/spec/fixtures/apis/orders/cancel/successful.yml +6 -0
  60. data/spec/fixtures/apis/payments/find/failed.yml +45 -0
  61. data/spec/fixtures/apis/payments/find/failed_with_message.yml +6 -0
  62. data/spec/fixtures/apis/payments/find/successful.yml +48 -0
  63. data/spec/fixtures/apis/payments/get/failed.yml +46 -0
  64. data/spec/fixtures/apis/payments/get/failed_with_message.yml +6 -0
  65. data/spec/fixtures/apis/payments/get/refunded.yml +49 -0
  66. data/spec/fixtures/apis/payments/get/successful.yml +49 -0
  67. data/spec/fixtures/apis/subscriptions/find/successful.yml +4 -4
  68. data/spec/fixtures/apis/subscriptions/get/successful.yml +31 -0
  69. data/spec/spec_helper.rb +13 -24
  70. data/spec/support/examples.rb +1 -0
  71. data/spec/support/helpers.rb +3 -2
  72. metadata +45 -15
  73. data/spec/cloud_payments/client_spec.rb +0 -5
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  require 'cloud_payments/namespaces/base'
2
3
  require 'cloud_payments/namespaces/cards'
3
4
  require 'cloud_payments/namespaces/tokens'
4
5
  require 'cloud_payments/namespaces/payments'
5
6
  require 'cloud_payments/namespaces/subscriptions'
6
7
  require 'cloud_payments/namespaces/orders'
8
+ require 'cloud_payments/namespaces/kassa'
9
+ require 'cloud_payments/namespaces/apple_pay'
7
10
 
8
11
  module CloudPayments
9
12
  module Namespaces
@@ -11,6 +14,10 @@ module CloudPayments
11
14
  Payments.new(self)
12
15
  end
13
16
 
17
+ def kassa
18
+ Kassa.new(self)
19
+ end
20
+
14
21
  def subscriptions
15
22
  Subscriptions.new(self)
16
23
  end
@@ -19,9 +26,13 @@ module CloudPayments
19
26
  Orders.new(self)
20
27
  end
21
28
 
29
+ def apple_pay
30
+ ApplePay.new(self)
31
+ end
32
+
22
33
  def ping
23
34
  !!(perform_request('/test').body || {})[:success]
24
- rescue ::Faraday::Error::ConnectionFailed, ::Faraday::Error::TimeoutError, CloudPayments::Client::ServerError => e
35
+ rescue ::Faraday::ConnectionFailed, ::Faraday::TimeoutError, CloudPayments::Client::ServerError => e
25
36
  false
26
37
  end
27
38
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module CloudPayments
3
+ module Namespaces
4
+ class ApplePay < Base
5
+ ValidationUrlMissing = Class.new(StandardError)
6
+
7
+ def self.resource_name
8
+ 'applepay'
9
+ end
10
+
11
+ def start_session(attributes)
12
+ validation_url = attributes.fetch(:validation_url) { raise ValidationUrlMissing.new('validation_url is required') }
13
+
14
+ request(:startsession, { "ValidationUrl" => validation_url })
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
3
  module Namespaces
3
4
  class Base
@@ -23,7 +24,7 @@ module CloudPayments
23
24
  protected
24
25
 
25
26
  def api_exceptions
26
- [::Faraday::Error::ConnectionFailed, ::Faraday::Error::TimeoutError, Client::ServerError, Client::GatewayError]
27
+ [::Faraday::ConnectionFailed, ::Faraday::TimeoutError, Client::ServerError, Client::GatewayError]
27
28
  end
28
29
 
29
30
  def resource_path(path = nil)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
3
  module Namespaces
3
4
  class Cards < Base
@@ -11,6 +12,11 @@ module CloudPayments
11
12
  instantiate(response[:model])
12
13
  end
13
14
 
15
+ def post3ds(attributes)
16
+ response = request(:post3ds, attributes)
17
+ instantiate(response[:model])
18
+ end
19
+
14
20
  private
15
21
 
16
22
  def instantiate(model)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module CloudPayments
3
+ module Namespaces
4
+ class Kassa < Base
5
+ InnNotProvided = Class.new(StandardError)
6
+ TypeNotProvided = Class.new(StandardError)
7
+ CustomerReceiptNotProvided = Class.new(StandardError)
8
+
9
+ def self.resource_name
10
+ 'kkt'
11
+ end
12
+
13
+ def receipt(attributes)
14
+ attributes.fetch(:inn) { raise InnNotProvided.new('inn attribute is required') }
15
+ attributes.fetch(:type) { raise TypeNotProvided.new('type attribute is required') }
16
+ attributes.fetch(:customer_receipt) { raise CustomerReceiptNotProvided.new('customer_receipt is required') }
17
+
18
+ request(:receipt, attributes)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
3
  module Namespaces
3
4
  class Orders < Base
@@ -5,6 +6,10 @@ module CloudPayments
5
6
  response = request(:create, attributes)
6
7
  Order.new(response[:model])
7
8
  end
9
+
10
+ def cancel(order_id)
11
+ request(:cancel, id: order_id)[:success]
12
+ end
8
13
  end
9
14
  end
10
15
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
3
  module Namespaces
3
4
  class Payments < Base
@@ -27,6 +28,16 @@ module CloudPayments
27
28
  response = request(:post3ds, transaction_id: id, pa_res: pa_res)
28
29
  Transaction.new(response[:model])
29
30
  end
31
+
32
+ def get(id)
33
+ response = request(:get, transaction_id: id)
34
+ Transaction.new(response[:model])
35
+ end
36
+
37
+ def find(invoice_id)
38
+ response = request(:find, invoice_id: invoice_id)
39
+ Transaction.new(response[:model])
40
+ end
30
41
  end
31
42
  end
32
43
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
3
  module Namespaces
3
4
  class Subscriptions < Base
@@ -6,6 +7,11 @@ module CloudPayments
6
7
  Subscription.new(response[:model])
7
8
  end
8
9
 
10
+ def find_all(account_id)
11
+ response = request(:find, account_id: account_id)
12
+ Array(response[:model]).map { |item| Subscription.new(item) }
13
+ end
14
+
9
15
  def create(attributes)
10
16
  response = request(:create, attributes)
11
17
  Subscription.new(response[:model])
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
3
  module Namespaces
3
4
  class Tokens < Base
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module CloudPayments
2
- VERSION = '0.0.3'
3
+ VERSION = '1.0.2'
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'openssl'
2
3
  require 'base64'
3
4
 
@@ -7,8 +8,8 @@ module CloudPayments
7
8
 
8
9
  attr_reader :config
9
10
 
10
- def initialize
11
- @config = CloudPayments.config
11
+ def initialize(config = nil)
12
+ @config = config || CloudPayments.config
12
13
  @digest = OpenSSL::Digest.new('sha256')
13
14
  @serializer = Client::Serializer::Base.new(config)
14
15
  end
@@ -22,6 +23,10 @@ module CloudPayments
22
23
  true
23
24
  end
24
25
 
26
+ def kassa_receipt(data)
27
+ OnKassaReceipt.new(@serializer.load(data))
28
+ end
29
+
25
30
  def on_recurrent(data)
26
31
  OnRecurrent.new(@serializer.load(data))
27
32
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe CloudPayments::Client::Response do
4
5
  let(:status){ 200 }
5
- let(:body){ '{"Model":{"Id":123,"CurrencyCode":"RUB","Amount":120},"Success":true}' }
6
+ let(:body){ '{"Model":{"Id":123,"CurrencyCode":"RUB","Amount":120},"Success":true}'.dup.force_encoding('CP1251').freeze }
6
7
  let(:headers){ { 'content-type' => 'application/json' } }
7
8
 
8
9
  subject{ CloudPayments::Client::Response.new(status, body, headers) }
@@ -13,6 +14,7 @@ describe CloudPayments::Client::Response do
13
14
  context 'wnen content type does not match /json/' do
14
15
  let(:headers){ { 'content-type' => 'text/plain' } }
15
16
  specify{ expect(subject.body).to eq(body) }
17
+ specify{ expect(subject.body.encoding.name).to eq('UTF-8') }
16
18
  end
17
19
  end
18
20
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe CloudPayments::Client::Serializer::MultiJson do
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
+ # frozen_string_literal: true
3
4
  require 'spec_helper'
4
5
 
5
6
  describe CloudPayments::Order do
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe CloudPayments::Secure3D do
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe CloudPayments::Subscription do
@@ -53,7 +54,6 @@ describe CloudPayments::Subscription do
53
54
 
54
55
  it_behaves_like :raise_without_attribute, :id
55
56
  it_behaves_like :raise_without_attribute, :account_id
56
- it_behaves_like :raise_without_attribute, :description
57
57
  it_behaves_like :raise_without_attribute, :email
58
58
  it_behaves_like :raise_without_attribute, :amount
59
59
  it_behaves_like :raise_without_attribute, :currency_code
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe CloudPayments::Transaction do
@@ -33,11 +34,13 @@ describe CloudPayments::Transaction do
33
34
  card_type: 'Visa',
34
35
  card_type_code: 0,
35
36
  card_exp_date: '10/17',
37
+ issuer: 'Sberbank of Russia',
36
38
  issuer_bank_country: 'RU',
37
39
  status: 'Completed',
38
40
  status_code: 3,
39
41
  reason: 'Approved',
40
42
  reason_code: 0,
43
+ refunded: false,
41
44
  card_holder_message: 'Payment successful',
42
45
  name: 'CARDHOLDER NAME',
43
46
  token: 'a4e67841-abb0-42de-a364-d1d8f9f4b3c0'
@@ -71,12 +74,14 @@ describe CloudPayments::Transaction do
71
74
  specify{ expect(subject.card_type).to eq('Visa') }
72
75
  specify{ expect(subject.card_type_code).to eq(0) }
73
76
  specify{ expect(subject.card_exp_date).to eq('10/17') }
77
+ specify{ expect(subject.issuer).to eq('Sberbank of Russia') }
74
78
  specify{ expect(subject.issuer_bank_country).to eq('RU') }
75
79
  specify{ expect(subject.reason).to eq('Approved') }
76
80
  specify{ expect(subject.reason_code).to eq(0) }
77
81
  specify{ expect(subject.card_holder_message).to eq('Payment successful') }
78
82
  specify{ expect(subject.name).to eq('CARDHOLDER NAME') }
79
83
  specify{ expect(subject.token).to eq('a4e67841-abb0-42de-a364-d1d8f9f4b3c0') }
84
+ specify{ expect(subject.refunded).to eq(false) }
80
85
 
81
86
  context 'without any attributes' do
82
87
  let(:attributes){ {} }
@@ -110,9 +115,11 @@ describe CloudPayments::Transaction do
110
115
  it_behaves_like :not_raise_without_attribute, :ip_longitude, :ip_lng
111
116
  it_behaves_like :not_raise_without_attribute, :card_type_code
112
117
  it_behaves_like :not_raise_without_attribute, :card_exp_date
118
+ it_behaves_like :not_raise_without_attribute, :issuer
113
119
  it_behaves_like :not_raise_without_attribute, :issuer_bank_country
114
120
  it_behaves_like :not_raise_without_attribute, :reason
115
121
  it_behaves_like :not_raise_without_attribute, :reason_code
122
+ it_behaves_like :not_raise_without_attribute, :refunded
116
123
  it_behaves_like :not_raise_without_attribute, :card_holder_message
117
124
  it_behaves_like :not_raise_without_attribute, :name
118
125
  it_behaves_like :not_raise_without_attribute, :token
@@ -205,7 +212,7 @@ describe CloudPayments::Transaction do
205
212
  subject{ CloudPayments::Transaction.new(default_attributes.merge(attributes)) }
206
213
 
207
214
  describe '#subscription' do
208
- before{ stub_api_request('subscriptions/find/successful').perform }
215
+ before{ stub_api_request('subscriptions/get/successful').perform }
209
216
 
210
217
  context 'with subscription_id' do
211
218
  let(:attributes){ { subscription_id: 'sc_8cf8a9338fb' } }
@@ -250,5 +257,22 @@ describe CloudPayments::Transaction do
250
257
  specify{ expect(subject.ip_location).to be_nil }
251
258
  end
252
259
  end
260
+
261
+ describe '#refunded?' do
262
+ context do
263
+ let(:attributes) { { refunded: false } }
264
+ specify { expect(subject.refunded?).to be_falsey }
265
+ end
266
+
267
+ context do
268
+ let(:attributes) { { refunded: true } }
269
+ specify { expect(subject.refunded?).to be_truthy }
270
+ end
271
+
272
+ context do
273
+ let(:attributes) { { refunded: nil } }
274
+ specify { expect(subject.refunded?).to be_falsey }
275
+ end
276
+ end
253
277
  end
254
278
  end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ # frozen_string_literal: true
4
+ require 'spec_helper'
5
+
6
+ describe CloudPayments::Namespaces::ApplePay do
7
+ subject{ described_class.new(CloudPayments.client) }
8
+
9
+ describe '#receipt' do
10
+ let(:attributes) do
11
+ {
12
+ validation_url: ''
13
+ }
14
+ end
15
+
16
+ context do
17
+ before{ attributes.delete(:validation_url) }
18
+ specify{ expect{subject.start_session(attributes)}.to raise_error(described_class::ValidationUrlMissing) }
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  class TestNamespace < CloudPayments::Namespaces::Base
@@ -14,8 +15,8 @@ describe CloudPayments::Namespaces::Base do
14
15
  subject{ TestNamespace.new(CloudPayments.client) }
15
16
 
16
17
  def stub_api(path, body = '')
17
- url = "http://user:pass@localhost:9292#{path}"
18
- stub_request(:post, url).with(body: body, headers: headers)
18
+ url = "http://localhost:9292#{path}"
19
+ stub_request(:post, url).with(body: body, headers: headers, basic_auth: ['user', 'pass'])
19
20
  end
20
21
 
21
22
  describe '#request' do
@@ -63,7 +64,13 @@ describe CloudPayments::Namespaces::Base do
63
64
 
64
65
  context 'config.raise_banking_errors = true' do
65
66
  before { CloudPayments.config.raise_banking_errors = true }
66
- specify{ expect{ subject.request(:path, request_params) }.to raise_error(CloudPayments::Client::GatewayErrors::LostCard) }
67
+ specify do
68
+ begin
69
+ subject.request(:path, request_params)
70
+ rescue CloudPayments::Client::GatewayErrors::LostCard => err
71
+ expect(err).to be_a CloudPayments::Client::ReasonedGatewayError
72
+ end
73
+ end
67
74
  end
68
75
 
69
76
  context 'config.raise_banking_errors = false' do
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  describe CloudPayments::Namespaces::Cards do
@@ -13,11 +14,11 @@ describe CloudPayments::Namespaces::Cards do
13
14
  card_cryptogram_packet: '01492500008719030128SM'
14
15
  } }
15
16
 
16
- context do
17
+ describe '#charge' do
17
18
  context 'config.raise_banking_errors = false' do
18
19
  before { CloudPayments.config.raise_banking_errors = false }
19
20
 
20
- describe '#charge' do
21
+ context do
21
22
  before{ stub_api_request('cards/charge/successful').perform }
22
23
  specify{ expect(subject.charge(attributes)).to be_instance_of(CloudPayments::Transaction) }
23
24
  specify{ expect(subject.charge(attributes)).not_to be_required_secure3d }
@@ -116,4 +117,43 @@ describe CloudPayments::Namespaces::Cards do
116
117
  end
117
118
  end
118
119
  end
120
+
121
+ describe '#post3ds' do
122
+ let(:attributes){ { transaction_id: 12345, pa_res: 'AQ==' } }
123
+
124
+ context 'config.raise_banking_errors = false' do
125
+ before { CloudPayments.config.raise_banking_errors = false }
126
+
127
+ context do
128
+ before{ stub_api_request('cards/post3ds/successful').perform }
129
+ specify{ expect(subject.post3ds(attributes)).to be_instance_of(CloudPayments::Transaction) }
130
+ specify{ expect(subject.post3ds(attributes)).not_to be_required_secure3d }
131
+ specify{ expect(subject.post3ds(attributes)).to be_completed }
132
+ specify{ expect(subject.post3ds(attributes).id).to eq(12345) }
133
+ end
134
+
135
+ context do
136
+ before{ stub_api_request('cards/post3ds/failed').perform }
137
+ specify{ expect(subject.post3ds(attributes)).to be_instance_of(CloudPayments::Transaction) }
138
+ specify{ expect(subject.post3ds(attributes)).not_to be_required_secure3d }
139
+ specify{ expect(subject.post3ds(attributes)).to be_declined }
140
+ specify{ expect(subject.post3ds(attributes).id).to eq(12345) }
141
+ specify{ expect(subject.post3ds(attributes).reason).to eq('InsufficientFunds') }
142
+ end
143
+ end
144
+
145
+ context 'config.raise_banking_errors = true' do
146
+ before { CloudPayments.config.raise_banking_errors = true }
147
+
148
+ context do
149
+ before{ stub_api_request('cards/post3ds/successful').perform }
150
+ specify{ expect{ subject.post3ds(attributes) }.not_to raise_error }
151
+ end
152
+
153
+ context do
154
+ before{ stub_api_request('cards/post3ds/failed').perform }
155
+ specify{ expect{ subject.post3ds(attributes) }.to raise_error(CloudPayments::Client::GatewayErrors::InsufficientFunds) }
156
+ end
157
+ end
158
+ end
119
159
  end