cloud_payments 0.0.3 → 1.0.2

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.
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