vsafe-ruby 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +241 -0
  3. data/Rakefile +6 -0
  4. data/lib/vsafe.rb +14 -0
  5. data/lib/vsafe/auth_result.rb +11 -0
  6. data/lib/vsafe/avs_result.rb +24 -0
  7. data/lib/vsafe/card_type.rb +29 -0
  8. data/lib/vsafe/charge_source.rb +12 -0
  9. data/lib/vsafe/client.rb +101 -0
  10. data/lib/vsafe/client_error.rb +4 -0
  11. data/lib/vsafe/config.rb +22 -0
  12. data/lib/vsafe/cvn_result.rb +17 -0
  13. data/lib/vsafe/payment_status.rb +15 -0
  14. data/lib/vsafe/request_error.rb +11 -0
  15. data/lib/vsafe/response.rb +47 -0
  16. data/lib/vsafe/response_error.rb +36 -0
  17. data/lib/vsafe/responses/charge_account_to_temporary_token.rb +12 -0
  18. data/lib/vsafe/responses/charge_authorize.rb +19 -0
  19. data/lib/vsafe/responses/charge_confirm.rb +10 -0
  20. data/lib/vsafe/responses/charge_sale.rb +20 -0
  21. data/lib/vsafe/responses/get_session_tags.rb +10 -0
  22. data/lib/vsafe/responses/reverse_payment.rb +12 -0
  23. data/lib/vsafe/responses/validate_charge_account.rb +19 -0
  24. data/lib/vsafe/version.rb +3 -0
  25. data/spec/spec_helper.rb +17 -0
  26. data/spec/vsafe/auth_result_spec.rb +12 -0
  27. data/spec/vsafe/avs_result_spec.rb +12 -0
  28. data/spec/vsafe/card_type_spec.rb +44 -0
  29. data/spec/vsafe/charge_source_spec.rb +12 -0
  30. data/spec/vsafe/client_spec.rb +186 -0
  31. data/spec/vsafe/config_spec.rb +14 -0
  32. data/spec/vsafe/cvn_result_spec.rb +12 -0
  33. data/spec/vsafe/payment_status_spec.rb +12 -0
  34. data/spec/vsafe/request_error_spec.rb +12 -0
  35. data/spec/vsafe/response_error_spec.rb +37 -0
  36. data/spec/vsafe/response_spec.rb +161 -0
  37. data/spec/vsafe/responses/charge_account_to_temporary_token_spec.rb +27 -0
  38. data/spec/vsafe/responses/charge_authorize_spec.rb +61 -0
  39. data/spec/vsafe/responses/charge_confirm_spec.rb +18 -0
  40. data/spec/vsafe/responses/charge_sale_spec.rb +68 -0
  41. data/spec/vsafe/responses/get_session_tags_spec.rb +27 -0
  42. data/spec/vsafe/responses/reverse_payment_spec.rb +37 -0
  43. data/spec/vsafe/responses/validate_charge_account.rb +19 -0
  44. data/spec/vsafe/vsafe_spec.rb +12 -0
  45. metadata +174 -0
@@ -0,0 +1,4 @@
1
+ module VSafe
2
+ class ClientError < ::StandardError
3
+ end
4
+ end
@@ -0,0 +1,22 @@
1
+ module VSafe
2
+ class Config
3
+ DEFAULT_REQUEST_TIMEOUT = 20 # seconds
4
+ DEFAULT_SANDBOX_URL = "https://paysafesandbox.ecustomersupport.com".freeze
5
+ DEFAULT_PRODUCTION_URL = "https://paysafe.ecustomerpayments.com".freeze
6
+
7
+ attr_accessor :sandbox,
8
+ :account_name,
9
+ :password,
10
+ :request_timeout
11
+
12
+ def initialize
13
+ # Set sandbox to true by default
14
+ @sandbox = true
15
+ @request_timeout = DEFAULT_REQUEST_TIMEOUT
16
+ end
17
+
18
+ def url
19
+ sandbox ? DEFAULT_SANDBOX_URL : DEFAULT_PRODUCTION_URL
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module VSafe
2
+ class CvnResult
3
+ INVALID = 15
4
+ MATCH = 17
5
+ NOT_MATCHED = 19
6
+ CHECK_NOT_PROCESSED = 20
7
+ SHOULD_BE_AVAILABLE = 22
8
+ NOT_SUPPORTED_BY_ISSUER = 23
9
+ NOT_SENT = 45
10
+
11
+ attr_reader :code
12
+
13
+ def initialize(code)
14
+ @code = code.to_i
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module VSafe
2
+ class PaymentStatus
3
+ BANK_DENIED = 1
4
+ VSAFE_PENDED = 2
5
+ VSAFE_DENIED = 3
6
+ AUTHORIZED = 5
7
+ SUCCESSFUL = 10
8
+
9
+ attr_reader :code
10
+
11
+ def initialize(code)
12
+ @code = code.to_i
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ require "vsafe/client_error"
2
+
3
+ module VSafe
4
+ class RequestError < ClientError
5
+ attr_reader :response
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ require "vsafe/response_error"
2
+ require "vsafe/request_error"
3
+
4
+ module VSafe
5
+ class Response
6
+ SUCCESS_RESPONSE_CODE = "0".freeze
7
+
8
+ attr_reader :response, :error
9
+
10
+ def self.define_attribute_mapping(attr_method, response_parameter, wrapper = nil)
11
+ define_method(attr_method) do
12
+ memo_variable = "@_#{attr_method}"
13
+ memo_value = instance_variable_get(memo_variable)
14
+ return memo_value if instance_variable_defined?(memo_variable)
15
+
16
+ memo_value = if wrapper && response.parsed_response[response_parameter]
17
+ wrapper.new(response.parsed_response[response_parameter])
18
+ else
19
+ response.parsed_response[response_parameter]
20
+ end
21
+
22
+ instance_variable_set(memo_variable, memo_value)
23
+ end
24
+ end
25
+
26
+ def initialize(response)
27
+ @response = response
28
+
29
+ if response.success?
30
+ unless success?
31
+ @error = ResponseError.new(response.parsed_response)
32
+ end
33
+ else
34
+ raise RequestError.new(response)
35
+ end
36
+ end
37
+
38
+ def success?
39
+ response.success? &&
40
+ response.parsed_response["ResponseCode"].to_s == SUCCESS_RESPONSE_CODE
41
+ end
42
+
43
+ def to_hash
44
+ response.parsed_response || {}
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ module VSafe
2
+ class ResponseError
3
+ GENERAL_SYSTEM_FAILURE = 1
4
+ PARAMETER_REQUIRED = 510
5
+ PARAMETER_EXCEEDS_MAX_LENGTH = 511
6
+ PARAMETER_IS_NOT_VALID = 512
7
+ PARAMETER_IS_INVALID = 514
8
+ LOGIN_FAILED = 1001
9
+ PAYMENT_NOT_FOUND = 1002
10
+ PAYMENT_IS_ALREADY_CONFIRMED = 1003
11
+ PAYMENT_NOT_AUTHORIZED = 1004
12
+ PARTNER_NOT_ACTIVE = 1005
13
+ AMOUNT_LARGER_THAN_ORIGINAL_AUTHORIZATION = 1006
14
+ AUTHORIZATION_IS_ALREADY_EXPIRED = 1007
15
+ ORIGINAL_TRANSACTION_WAS_FROM_A_DIFFERENT_INTERFACE = 1008
16
+
17
+ SUGGESTED_MESSAGES = {
18
+ GENERAL_SYSTEM_FAILURE => "System not available. Please try again later.".freeze,
19
+ PARAMETER_IS_INVALID => "Please select a correct value.".freeze,
20
+ LOGIN_FAILED => "System not available. Please try again later.".freeze,
21
+ PARTNER_NOT_ACTIVE => "System not available. Please try again later.".freeze,
22
+ AUTHORIZATION_IS_ALREADY_EXPIRED => "Please provide your payment information again.".freeze,
23
+ ORIGINAL_TRANSACTION_WAS_FROM_A_DIFFERENT_INTERFACE => "Please try again later.".freeze
24
+ }.freeze
25
+
26
+ attr_reader :code,
27
+ :text,
28
+ :suggested_message
29
+
30
+ def initialize(response_body)
31
+ @code = response_body["ResponseCode"].to_i
32
+ @text = response_body["ResponseText"]
33
+ @suggested_message = SUGGESTED_MESSAGES[code]
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ require "vsafe/response"
2
+ require "vsafe/card_type"
3
+
4
+ module VSafe
5
+ module Responses
6
+ class ChargeAccountToTemporaryToken < Response
7
+ define_attribute_mapping(:token, "ChargeAccountNumberToken")
8
+ define_attribute_mapping(:last4, "PaymentDeviceLast4")
9
+ define_attribute_mapping(:card_type, "PaymentDeviceTypeCD", CardType)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ require "vsafe/response"
2
+ require "vsafe/avs_result"
3
+ require "vsafe/cvn_result"
4
+ require "vsafe/payment_status"
5
+ require "vsafe/auth_result"
6
+
7
+ module VSafe
8
+ module Responses
9
+ class ChargeAuthorize < Response
10
+ define_attribute_mapping(:avs_result, "AVSResultCode", AvsResult)
11
+ define_attribute_mapping(:auth_result, "AuthResultCode", AuthResult)
12
+ define_attribute_mapping(:cvn_result, "CVNResultCode", CvnResult)
13
+ define_attribute_mapping(:charge_permanent_token, "ChargePermanentToken")
14
+ define_attribute_mapping(:payment_acquirer_name, "PaymentAcquirerName")
15
+ define_attribute_mapping(:payment_id, "PaymentID")
16
+ define_attribute_mapping(:payment_status, "PaymentStatus", PaymentStatus)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require "vsafe/response"
2
+ require "vsafe/payment_status"
3
+
4
+ module VSafe
5
+ module Responses
6
+ class ChargeConfirm < Response
7
+ define_attribute_mapping(:payment_status, "PaymentStatus", PaymentStatus)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ require "vsafe/response"
2
+ require "vsafe/avs_result"
3
+ require "vsafe/cvn_result"
4
+ require "vsafe/payment_status"
5
+ require "vsafe/auth_result"
6
+
7
+ module VSafe
8
+ module Responses
9
+ class ChargeSale < Response
10
+ define_attribute_mapping(:avs_result, "AVSResultCode", AvsResult)
11
+ define_attribute_mapping(:auth_result, "AuthResultCode", AuthResult)
12
+ define_attribute_mapping(:cvn_result, "CVNResultCode", CvnResult)
13
+ define_attribute_mapping(:charge_permanent_token, "ChargePermanentToken")
14
+ define_attribute_mapping(:payment_acquirer_name, "PaymentAcquirerName")
15
+ define_attribute_mapping(:payment_id, "PaymentID")
16
+ define_attribute_mapping(:payment_id_pk, "PaymentID_pk")
17
+ define_attribute_mapping(:payment_status, "PaymentStatus", PaymentStatus)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ require "vsafe/response"
2
+
3
+ module VSafe
4
+ module Responses
5
+ class GetSessionTags < Response
6
+ define_attribute_mapping(:org_id, "OrgID")
7
+ define_attribute_mapping(:web_session_id, "WebSessionID")
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ require "vsafe/response"
2
+ require "vsafe/payment_status"
3
+
4
+ module VSafe
5
+ module Responses
6
+ class ReversePayment < Response
7
+ define_attribute_mapping(:available_refund_amount, "AvailableRefundAmount")
8
+ define_attribute_mapping(:payment_acquirer_name, "PaymentAcquirerName")
9
+ define_attribute_mapping(:payment_id, "PaymentID")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ require "vsafe/response"
2
+ require "vsafe/avs_result"
3
+ require "vsafe/auth_result"
4
+ require "vsafe/cvn_result"
5
+ require "vsafe/payment_status"
6
+
7
+ module VSafe
8
+ module Responses
9
+ class ValidateChargeAccount < Response
10
+ define_attribute_mapping(:avs_result, "AVSResultCode", AvsResult)
11
+ define_attribute_mapping(:auth_result, "AuthResultCode", AuthResult)
12
+ define_attribute_mapping(:cvn_result, "CVNResultCode", CvnResult)
13
+ define_attribute_mapping(:charge_permanent_token, "ChargePermanentToken")
14
+ define_attribute_mapping(:payment_acquirer_name, "PaymentAcquirerName")
15
+ define_attribute_mapping(:payment_id, "PaymentID")
16
+ define_attribute_mapping(:payment_status, "PaymentStatus", PaymentStatus)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module VSafe
2
+ VERSION = "0.2.2"
3
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require "vsafe"
3
+ require "webmock/rspec"
4
+ require "byebug"
5
+
6
+ RSpec.configure do |config|
7
+ # Run specs in random order to surface order dependencies. If you find an
8
+ # order dependency and want to debug it, you can fix the order by providing
9
+ # the seed, which is printed after each run.
10
+ # --seed 1234
11
+ config.order = "random"
12
+
13
+ # Disable the should syntax compeletely; we use the expect syntax only.
14
+ config.expect_with :rspec do |c|
15
+ c.syntax = :expect
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+ require "vsafe/auth_result"
3
+
4
+ RSpec.describe VSafe::AuthResult do
5
+ describe ".new" do
6
+ let(:code) { 1 }
7
+
8
+ it "sets the code" do
9
+ expect(VSafe::AuthResult.new(code.to_s).code).to eq(code)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+ require "vsafe/avs_result"
3
+
4
+ RSpec.describe VSafe::AvsResult do
5
+ describe ".new" do
6
+ let(:code) { 1 }
7
+
8
+ it "returns code" do
9
+ expect(VSafe::AvsResult.new(code.to_s).code).to eq(code)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+ require "vsafe/card_type"
3
+
4
+ RSpec.describe VSafe::CardType do
5
+ describe ".new" do
6
+ let(:code) { 1 }
7
+
8
+ it "sets the code" do
9
+ expect(VSafe::CardType.new(code.to_s).code).to eq(code)
10
+ end
11
+ end
12
+
13
+ describe ".description" do
14
+ let(:card_type_codes) {
15
+ [
16
+ VSafe::CardType::AMERICAN_EXPRESS,
17
+ VSafe::CardType::VISA,
18
+ VSafe::CardType::MASTERCARD,
19
+ VSafe::CardType::DISCOVER,
20
+ VSafe::CardType::DINERS_CLUB,
21
+ VSafe::CardType::OPTIMA
22
+ ]
23
+ }
24
+
25
+ let(:card_type_descriptions) {
26
+ [
27
+ "American Express",
28
+ "Visa",
29
+ "MasterCard",
30
+ "Discover",
31
+ "Diners Club",
32
+ "Optima"
33
+ ]
34
+ }
35
+
36
+ it "returns description" do
37
+ index = 0
38
+ card_type_codes.each do |code|
39
+ expect(VSafe::CardType.new(code).description).to eq(card_type_descriptions[index])
40
+ index += 1
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+ require "vsafe/charge_source"
3
+
4
+ RSpec.describe VSafe::ChargeSource do
5
+ describe "constants" do
6
+ it "returns correct string" do
7
+ expect(VSafe::ChargeSource::PPD).to eq("PPD")
8
+ expect(VSafe::ChargeSource::TEL).to eq("TEL")
9
+ expect(VSafe::ChargeSource::WEB).to eq("WEB")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,186 @@
1
+ require "spec_helper"
2
+ require "vsafe/client"
3
+ require "vsafe/config"
4
+ require "vsafe/response"
5
+ require "httparty"
6
+
7
+ RSpec.describe VSafe::Client do
8
+ let(:body) {
9
+ {
10
+ ResponseCode: VSafe::Response::SUCCESS_RESPONSE_CODE
11
+ }.to_json
12
+ }
13
+ let(:client) { VSafe::Client.new }
14
+ let(:params) { {"test" => 1} }
15
+
16
+ def assert_vsafe_request(request)
17
+ expect(request.headers["Content-Type"]).to eq(VSafe::Client::REQUEST_CONTENT_TYPE)
18
+
19
+ json = JSON.parse(request.body)
20
+ expect(json["AccountName"]).to eq(client.config.account_name)
21
+ expect(json["Password"]).to eq(client.config.password)
22
+
23
+ json
24
+ end
25
+
26
+ def stub_vsafe_request(endpoint)
27
+ stub_request(:post, client.service_url(endpoint)) do |request|
28
+ json = assert_vsafe_request(request)
29
+
30
+ params.each do |key, value|
31
+ expect(json[key]).to eq(value)
32
+ end
33
+ end.to_return(body: body, headers: { "Content-Type" => VSafe::Client::REQUEST_CONTENT_TYPE })
34
+ end
35
+
36
+ describe ".new" do
37
+ context "when block is given" do
38
+ it "yields block" do
39
+ yielded_config = nil
40
+
41
+ client = VSafe::Client.new do |config|
42
+ yielded_config = config
43
+ end
44
+
45
+ expect(yielded_config).to be_a(VSafe::Config)
46
+ expect(yielded_config).to eq(client.config)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#get_session_tags" do
52
+ it "returns response" do
53
+ stub_request(:post, client.service_url("GetSessionTags")) do |request|
54
+ json = assert_vsafe_request(request)
55
+ expect(json["TransactionID"]).not_to be_nil
56
+ end.to_return(body: body, headers: { "Content-Type" => VSafe::Client::REQUEST_CONTENT_TYPE })
57
+
58
+ response = client.get_session_tags
59
+
60
+ expect(response).to be_a(VSafe::Responses::GetSessionTags)
61
+ expect(response).to be_success
62
+ end
63
+ end
64
+
65
+ describe "#charge_authorize" do
66
+ it "returns response" do
67
+ stub_vsafe_request("ChargeAuthorize")
68
+
69
+ response = client.charge_authorize(params)
70
+
71
+ expect(response).to be_a(VSafe::Responses::ChargeAuthorize)
72
+ expect(response).to be_success
73
+ end
74
+ end
75
+
76
+ describe "#charge_confirm" do
77
+ it "returns response" do
78
+ stub_vsafe_request("ChargeConfirm")
79
+
80
+ response = client.charge_confirm(params)
81
+
82
+ expect(response).to be_a(VSafe::Responses::ChargeConfirm)
83
+ expect(response).to be_success
84
+ end
85
+ end
86
+
87
+ describe "#reverse_payment" do
88
+ it "returns response" do
89
+ stub_vsafe_request("ReversePayment")
90
+
91
+ response = client.reverse_payment(params)
92
+
93
+ expect(response).to be_a(VSafe::Responses::ReversePayment)
94
+ expect(response).to be_success
95
+ end
96
+ end
97
+
98
+ describe "#heartbeat" do
99
+ it "returns response" do
100
+ stub_request(:post, client.service_url("HeartBeat")) do |request|
101
+ json = assert_vsafe_request(request)
102
+ end.to_return(body: body, headers: { "Content-Type" => VSafe::Client::REQUEST_CONTENT_TYPE })
103
+
104
+ response = client.heartbeat
105
+
106
+ expect(response).to be_a(VSafe::Response)
107
+ expect(response).to be_success
108
+ end
109
+ end
110
+
111
+ describe "#charge_sale" do
112
+ it "returns response" do
113
+ stub_vsafe_request("ChargeSale")
114
+
115
+ response = client.charge_sale(params)
116
+
117
+ expect(response).to be_a(VSafe::Responses::ChargeSale)
118
+ expect(response).to be_success
119
+ end
120
+ end
121
+
122
+ describe "#validate_charge_account" do
123
+ it "returns response" do
124
+ stub_vsafe_request("ValidateChargeAccount")
125
+
126
+ response = client.validate_charge_account(params)
127
+
128
+ expect(response).to be_a(VSafe::Responses::ValidateChargeAccount)
129
+ expect(response).to be_success
130
+ end
131
+ end
132
+
133
+ describe "#service_url" do
134
+ shared_examples_for "returns url" do |jsonp|
135
+ context "when endpoint defined" do
136
+ let(:endpoint) { "foo" }
137
+
138
+ it "returns endpoint url" do
139
+ expect(client.service_url(endpoint, jsonp)).to eq("#{client.config.url}/#{service_path}/#{endpoint}")
140
+ end
141
+ end
142
+
143
+ describe "when endpoint not defined" do
144
+ let(:endpoint) { nil }
145
+
146
+ it "returns base url" do
147
+ expect(client.service_url(endpoint, jsonp)).to eq("#{client.config.url}/#{service_path}")
148
+ end
149
+ end
150
+ end
151
+
152
+ context "when jsonp is false" do
153
+ let(:service_path) { VSafe::Client::SERVICE_PATH }
154
+
155
+ it_behaves_like "returns url"
156
+ end
157
+
158
+ context "when jsonp is true" do
159
+ let(:service_path) { VSafe::Client::JSONP_SERVICE_PATH }
160
+
161
+ it_behaves_like "returns url", true
162
+ end
163
+ end
164
+
165
+ describe "#fingerprint_url" do
166
+ context "when sandbox" do
167
+ before do
168
+ allow(client.config).to receive(:sandbox).and_return(true)
169
+ end
170
+
171
+ it "returns url" do
172
+ expect(client.fingerprint_url).to eq("#{client.config.url}/#{VSafe::Client::SANDBOX_FINGERPRINT_PATH}")
173
+ end
174
+ end
175
+
176
+ context "when not sandbox" do
177
+ before do
178
+ allow(client.config).to receive(:sandbox).and_return(false)
179
+ end
180
+
181
+ it "returns url" do
182
+ expect(client.fingerprint_url).to eq("#{client.config.url}/#{VSafe::Client::FINGERPRINT_PATH}")
183
+ end
184
+ end
185
+ end
186
+ end