vsafe-ruby 0.2.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 (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