cloud_payments 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +8 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +106 -0
  8. data/Rakefile +7 -0
  9. data/cloud_payments.gemspec +28 -0
  10. data/config.ru +58 -0
  11. data/lib/cloud_payments.rb +34 -0
  12. data/lib/cloud_payments/client.rb +50 -0
  13. data/lib/cloud_payments/client/errors.rb +68 -0
  14. data/lib/cloud_payments/client/gateway_errors.rb +36 -0
  15. data/lib/cloud_payments/client/response.rb +22 -0
  16. data/lib/cloud_payments/client/serializer.rb +9 -0
  17. data/lib/cloud_payments/client/serializer/base.rb +46 -0
  18. data/lib/cloud_payments/client/serializer/multi_json.rb +17 -0
  19. data/lib/cloud_payments/config.rb +44 -0
  20. data/lib/cloud_payments/models.rb +4 -0
  21. data/lib/cloud_payments/models/model.rb +5 -0
  22. data/lib/cloud_payments/models/secure3d.rb +15 -0
  23. data/lib/cloud_payments/models/subscription.rb +49 -0
  24. data/lib/cloud_payments/models/transaction.rb +82 -0
  25. data/lib/cloud_payments/namespaces.rb +23 -0
  26. data/lib/cloud_payments/namespaces/base.rb +50 -0
  27. data/lib/cloud_payments/namespaces/cards.rb +25 -0
  28. data/lib/cloud_payments/namespaces/payments.rb +32 -0
  29. data/lib/cloud_payments/namespaces/subscriptions.rb +24 -0
  30. data/lib/cloud_payments/namespaces/tokens.rb +15 -0
  31. data/lib/cloud_payments/version.rb +3 -0
  32. data/spec/cloud_payments/client/response_spec.rb +35 -0
  33. data/spec/cloud_payments/client/serializer/multi_json_spec.rb +16 -0
  34. data/spec/cloud_payments/client_spec.rb +5 -0
  35. data/spec/cloud_payments/models/secure3d_spec.rb +37 -0
  36. data/spec/cloud_payments/models/subscription_spec.rb +141 -0
  37. data/spec/cloud_payments/models/transaction_spec.rb +254 -0
  38. data/spec/cloud_payments/namespaces/base_spec.rb +75 -0
  39. data/spec/cloud_payments/namespaces/cards_spec.rb +119 -0
  40. data/spec/cloud_payments/namespaces/payments_spec.rb +96 -0
  41. data/spec/cloud_payments/namespaces/subscriptions_spec.rb +82 -0
  42. data/spec/cloud_payments/namespaces/tokens_spec.rb +90 -0
  43. data/spec/cloud_payments/namespaces_spec.rb +45 -0
  44. data/spec/cloud_payments_spec.rb +14 -0
  45. data/spec/fixtures/apis/cards/auth/failed.yml +45 -0
  46. data/spec/fixtures/apis/cards/auth/secure3d.yml +15 -0
  47. data/spec/fixtures/apis/cards/auth/successful.yml +48 -0
  48. data/spec/fixtures/apis/cards/charge/failed.yml +45 -0
  49. data/spec/fixtures/apis/cards/charge/secure3d.yml +15 -0
  50. data/spec/fixtures/apis/cards/charge/successful.yml +48 -0
  51. data/spec/fixtures/apis/payments/confirm/failed.yml +6 -0
  52. data/spec/fixtures/apis/payments/confirm/failed_with_message.yml +6 -0
  53. data/spec/fixtures/apis/payments/confirm/successful.yml +6 -0
  54. data/spec/fixtures/apis/payments/post3ds/failed.yml +45 -0
  55. data/spec/fixtures/apis/payments/post3ds/successful.yml +48 -0
  56. data/spec/fixtures/apis/payments/refund/failed.yml +6 -0
  57. data/spec/fixtures/apis/payments/refund/failed_with_message.yml +6 -0
  58. data/spec/fixtures/apis/payments/refund/successful.yml +6 -0
  59. data/spec/fixtures/apis/payments/void/failed.yml +6 -0
  60. data/spec/fixtures/apis/payments/void/failed_with_message.yml +6 -0
  61. data/spec/fixtures/apis/payments/void/successful.yml +6 -0
  62. data/spec/fixtures/apis/ping/failed.yml +5 -0
  63. data/spec/fixtures/apis/ping/successful.yml +5 -0
  64. data/spec/fixtures/apis/subscriptions/cancel/successful.yml +6 -0
  65. data/spec/fixtures/apis/subscriptions/create/successful.yml +31 -0
  66. data/spec/fixtures/apis/subscriptions/find/successful.yml +31 -0
  67. data/spec/fixtures/apis/subscriptions/update/successful.yml +31 -0
  68. data/spec/fixtures/apis/tokens/auth/failed.yml +45 -0
  69. data/spec/fixtures/apis/tokens/auth/successful.yml +48 -0
  70. data/spec/fixtures/apis/tokens/charge/failed.yml +45 -0
  71. data/spec/fixtures/apis/tokens/charge/successful.yml +48 -0
  72. data/spec/spec_helper.rb +38 -0
  73. data/spec/support/examples.rb +27 -0
  74. data/spec/support/helpers.rb +89 -0
  75. metadata +244 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 02f3745fb8ba27279fbc61b8457ea085e80a423e
4
+ data.tar.gz: df63c5180c52eb51cfc4a59e009b428cc6b6c3bd
5
+ SHA512:
6
+ metadata.gz: 52aea99410e9b050a7ed070e9b5642dba502fafa12a4ff034450aa74e319cf1f6767aa38f70399b42b466650da0896599ce234c843cc3eb37e884b657cd30605
7
+ data.tar.gz: 2f8a19f58a38a67a62aa691b36382f3d23f67d9edee409f7d8d126eb840cc125f8fd81ad30daed3bc54e24526161655b74de76f3d6e8b51cbff17252e67a0fde
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format progress
2
+ --color
3
+ --backtrace
4
+ --order random
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ env:
3
+ - CODECLIMATE_REPO_TOKEN=8e9b89269d9aafc2dec2706a43825201de496b743505d7a7666068f7b22b07d
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.3
8
+ - rbx-2
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cloud_payments.gemspec
4
+ gemspec
5
+
6
+ gem 'oj'
7
+ gem 'pry'
8
+ gem 'rack'
9
+ gem 'webmock', require: 'webmock/rspec'
10
+ gem "codeclimate-test-reporter", group: :test, require: nil
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 undr
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,106 @@
1
+ # CloudPayments
2
+
3
+ CloudPayments ruby client (http://cloudpayments.eu/Docs/Integration)
4
+
5
+ [![Build Status](https://travis-ci.org/undr/cloud_payments.svg)](https://travis-ci.org/undr/cloud_payments)
6
+ [![Code Climate](https://codeclimate.com/github/undr/cloud_payments/badges/gpa.svg)](https://codeclimate.com/github/undr/cloud_payments)
7
+ [![Test Coverage](https://codeclimate.com/github/undr/cloud_payments/badges/coverage.svg)](https://codeclimate.com/github/undr/cloud_payments)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'cloud_payments'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```
20
+ $ bundle
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```
26
+ $ gem install cloud_payments
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Configuration
32
+
33
+ ```ruby
34
+ CloudPayments.configure do |c|
35
+ c.host = 'http://localhost:3000' # By default, it is https://api.cloudpayments.ru
36
+ c.public_key = ''
37
+ c.secret_key = ''
38
+ c.log = false # By default. it is true
39
+ c.logger = Logger.new('/dev/null') # By default, it writes logs to stdout
40
+ c.raise_banking_errors = true # By default, it is not raising banking errors
41
+ end
42
+ ```
43
+
44
+ ### Test method
45
+
46
+ ```ruby
47
+ CloudPayments.client.ping
48
+ # => true
49
+ ```
50
+
51
+ ### Cryptogram-based payments
52
+
53
+ ```ruby
54
+ transaction = CloudPayments.client.payments.cards.charge(
55
+ amount: 120,
56
+ currency: 'RUB',
57
+ ip_address: request.remote_ip,
58
+ name: params[:name],
59
+ card_cryptogram_packet: params[:card_cryptogram_packet]
60
+ )
61
+ # => {:metadata=>nil,
62
+ # :id=>12345,
63
+ # :amount=>120,
64
+ # :currency=>"RUB",
65
+ # :currency_code=>0,
66
+ # :invoice_id=>"1234567",
67
+ # :account_id=>"user_x",
68
+ # :email=>nil,
69
+ # :description=>"Payment for goods on example.com",
70
+ # :created_at=>#<DateTime: 2014-08-09T11:49:41+00:00 ((2456879j,42581s,0n),+0s,2299161j)>,
71
+ # :authorized_at=>#<DateTime: 2014-08-09T11:49:42+00:00 ((2456879j,42582s,0n),+0s,2299161j)>,
72
+ # :confirmed_at=>#<DateTime: 2014-08-09T11:49:42+00:00 ((2456879j,42582s,0n),+0s,2299161j)>,
73
+ # :auth_code=>"123456",
74
+ # :test_mode=>true,
75
+ # :ip_address=>"195.91.194.13",
76
+ # :ip_country=>"RU",
77
+ # :ip_city=>"Ufa",
78
+ # :ip_region=>"Bashkortostan Republic",
79
+ # :ip_district=>"Volga Federal District",
80
+ # :ip_lat=>54.7355,
81
+ # :ip_lng=>55.991982,
82
+ # :card_first_six=>"411111",
83
+ # :card_last_four=>"1111",
84
+ # :card_type=>"Visa",
85
+ # :card_type_code=>0,
86
+ # :issuer_bank_country=>"RU",
87
+ # :status=>"Completed",
88
+ # :status_code=>3,
89
+ # :reason=>"Approved",
90
+ # :reason_code=>0,
91
+ # :card_holder_message=>"Payment successful",
92
+ # :name=>"CARDHOLDER NAME",
93
+ # :token=>"a4e67841-abb0-42de-a364-d1d8f9f4b3c0"}
94
+ transaction.class
95
+ # => CloudPayments::Transaction
96
+ transaction.token
97
+ # => "a4e67841-abb0-42de-a364-d1d8f9f4b3c0"
98
+ ```
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it ( https://github.com/undr/cloud_payments/fork )
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create a new Pull Request
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
7
+
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloud_payments/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cloud_payments'
8
+ spec.version = CloudPayments::VERSION
9
+ spec.authors = ['undr']
10
+ spec.email = ['undr@yandex.ru']
11
+ spec.summary = %q{CloudPayments ruby client}
12
+ spec.description = %q{CloudPayments ruby client}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}){|f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'faraday'
22
+ spec.add_dependency 'multi_json'
23
+ spec.add_dependency 'hashie'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.7'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec'
28
+ end
@@ -0,0 +1,58 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'cloud_payments'
3
+ require 'rack'
4
+ require 'pp'
5
+
6
+ app = ->(env){
7
+ pp env
8
+ pp Rack::Request.new(env).params
9
+
10
+ json = <<-JSON
11
+ {
12
+ "Model":{
13
+ "TransactionId": 12345,
14
+ "Amount": 120,
15
+ "Currency": "RUB",
16
+ "CurrencyCode": 0,
17
+ "InvoiceId": "1234567",
18
+ "AccountId": "user_x",
19
+ "Email": null,
20
+ "Description": "Payment for goods on example.com",
21
+ "JsonData": null,
22
+ "CreatedDate": "\/Date(1401718880000)\/",
23
+ "CreatedDateIso":"2014-08-09T11:49:41",
24
+ "AuthDate": "\/Date(1401733880523)\/",
25
+ "AuthDateIso":"2014-08-09T11:49:42",
26
+ "ConfirmDate": "\/Date(1401733880523)\/",
27
+ "ConfirmDateIso":"2014-08-09T11:49:42",
28
+ "AuthCode": "123456",
29
+ "TestMode": true,
30
+ "IpAddress": "195.91.194.13",
31
+ "IpCountry": "RU",
32
+ "IpCity": "Ufa",
33
+ "IpRegion": "Bashkortostan Republic",
34
+ "IpDistrict": "Volga Federal District",
35
+ "IpLatitude": 54.7355,
36
+ "IpLongitude": 55.991982,
37
+ "CardFirstSix": "411111",
38
+ "CardLastFour": "1111",
39
+ "CardType": "Visa",
40
+ "CardTypeCode": 0,
41
+ "IssuerBankCountry": "RU",
42
+ "Status": "Completed",
43
+ "StatusCode": 3,
44
+ "Reason": "Approved",
45
+ "ReasonCode": 0,
46
+ "CardHolderMessage": "Payment successful",
47
+ "Name": "CARDHOLDER NAME",
48
+ "Token": "a4e67841-abb0-42de-a364-d1d8f9f4b3c0"
49
+ },
50
+ "Success":true,
51
+ "Message":null
52
+ }
53
+ JSON
54
+
55
+ [200, { 'Content-Type' => 'application/json' }, [json]]
56
+ }
57
+
58
+ run app
@@ -0,0 +1,34 @@
1
+ require 'date'
2
+ require 'hashie'
3
+ require 'faraday'
4
+ require 'multi_json'
5
+ require 'pp'
6
+ require 'cloud_payments/version'
7
+ require 'cloud_payments/config'
8
+ require 'cloud_payments/namespaces'
9
+ require 'cloud_payments/models'
10
+ require 'cloud_payments/client'
11
+
12
+ module CloudPayments
13
+ extend self
14
+
15
+ def config=(value)
16
+ @config = value
17
+ end
18
+
19
+ def config
20
+ @config ||= Config.new
21
+ end
22
+
23
+ def configure
24
+ yield config
25
+ end
26
+
27
+ def client=(value)
28
+ @client = value
29
+ end
30
+
31
+ def client
32
+ @client ||= Client.new
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ require 'cloud_payments/client/errors'
2
+ require 'cloud_payments/client/gateway_errors'
3
+ require 'cloud_payments/client/response'
4
+ require 'cloud_payments/client/serializer'
5
+
6
+ module CloudPayments
7
+ class Client
8
+ include Namespaces
9
+
10
+ attr_reader :config, :connection
11
+
12
+ def initialize
13
+ @config = CloudPayments.config
14
+ @connection = build_connection
15
+ end
16
+
17
+ def perform_request(path, params = nil)
18
+ connection.basic_auth(config.public_key, config.secret_key)
19
+ response = connection.post(path, (params ? convert_to_json(params) : nil), headers)
20
+
21
+ Response.new(response.status, response.body, response.headers).tap do |response|
22
+ raise_transport_error(response) if response.status.to_i >= 300
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def convert_to_json(data)
29
+ config.serializer.dump(data)
30
+ end
31
+
32
+ def headers
33
+ { 'Content-Type' => 'application/json' }
34
+ end
35
+
36
+ def logger
37
+ config.logger
38
+ end
39
+
40
+ def raise_transport_error(response)
41
+ logger.fatal "[#{response.status}] #{response.origin_body}" if logger
42
+ error = ERRORS[response.status] || ServerError
43
+ raise error.new "[#{response.status}] #{response.origin_body}"
44
+ end
45
+
46
+ def build_connection
47
+ Faraday::Connection.new(config.host, config.connection_options, &config.connection_block)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,68 @@
1
+ module CloudPayments
2
+ class Client
3
+ class Error < StandardError; end
4
+ class ServerError < StandardError; end
5
+ class GatewayError < StandardError
6
+ attr_reader :body
7
+
8
+ def initialize(message, body)
9
+ super(message)
10
+ @body = body
11
+ end
12
+ end
13
+
14
+ module Errors; end
15
+
16
+ HTTP_STATUSES = {
17
+ 300 => 'MultipleChoices',
18
+ 301 => 'MovedPermanently',
19
+ 302 => 'Found',
20
+ 303 => 'SeeOther',
21
+ 304 => 'NotModified',
22
+ 305 => 'UseProxy',
23
+ 307 => 'TemporaryRedirect',
24
+ 308 => 'PermanentRedirect',
25
+
26
+ 400 => 'BadRequest',
27
+ 401 => 'Unauthorized',
28
+ 402 => 'PaymentRequired',
29
+ 403 => 'Forbidden',
30
+ 404 => 'NotFound',
31
+ 405 => 'MethodNotAllowed',
32
+ 406 => 'NotAcceptable',
33
+ 407 => 'ProxyAuthenticationRequired',
34
+ 408 => 'RequestTimeout',
35
+ 409 => 'Conflict',
36
+ 410 => 'Gone',
37
+ 411 => 'LengthRequired',
38
+ 412 => 'PreconditionFailed',
39
+ 413 => 'RequestEntityTooLarge',
40
+ 414 => 'RequestURITooLong',
41
+ 415 => 'UnsupportedMediaType',
42
+ 416 => 'RequestedRangeNotSatisfiable',
43
+ 417 => 'ExpectationFailed',
44
+ 418 => 'ImATeapot',
45
+ 421 => 'TooManyConnectionsFromThisIP',
46
+ 426 => 'UpgradeRequired',
47
+ 450 => 'BlockedByWindowsParentalControls',
48
+ 494 => 'RequestHeaderTooLarge',
49
+ 497 => 'HTTPToHTTPS',
50
+ 499 => 'ClientClosedRequest',
51
+
52
+ 500 => 'InternalServerError',
53
+ 501 => 'NotImplemented',
54
+ 502 => 'BadGateway',
55
+ 503 => 'ServiceUnavailable',
56
+ 504 => 'GatewayTimeout',
57
+ 505 => 'HTTPVersionNotSupported',
58
+ 506 => 'VariantAlsoNegotiates',
59
+ 510 => 'NotExtended'
60
+ }
61
+
62
+ ERRORS = HTTP_STATUSES.inject({}) do |result, error|
63
+ status, name = error
64
+ result[status] = Errors.const_set(name, Class.new(ServerError))
65
+ result
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,36 @@
1
+ module CloudPayments
2
+ class Client
3
+ module GatewayErrors; end
4
+
5
+ REASON_CODES = {
6
+ 5001 => 'ReferToCardIssuer',
7
+ 5005 => 'DoNotHonor',
8
+ 5006 => 'Error',
9
+ 5012 => 'Invalid',
10
+ 5013 => 'AmountError',
11
+ 5030 => 'FormatError',
12
+ 5031 => 'BankNotSupportedBySwitch',
13
+ 5034 => 'SuspectedFraud',
14
+ 5041 => 'LostCard',
15
+ 5043 => 'StolenCard',
16
+ 5051 => 'InsufficientFunds',
17
+ 5054 => 'ExpiredCard',
18
+ 5057 => 'TransactionNotPermitted',
19
+ 5065 => 'ExceedWithdrawalFrequency',
20
+ 5082 => 'IncorrectCVV',
21
+ 5091 => 'Timeout',
22
+ 5092 => 'CannotReachNetwork',
23
+ 5096 => 'SystemError',
24
+ 5204 => 'UnableToProcess',
25
+ 5206 => 'AuthenticationFailed',
26
+ 5207 => 'AuthenticationUnavailable',
27
+ 5300 => 'AntiFraud'
28
+ }
29
+
30
+ GATEWAY_ERRORS = REASON_CODES.inject({}) do |result, error|
31
+ status, name = error
32
+ result[status] = GatewayErrors.const_set(name, Class.new(StandardError))
33
+ result
34
+ end
35
+ end
36
+ end