active_merchant-epsilon 0.5.1

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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/.env.sample +3 -0
  3. data/.gitignore +17 -0
  4. data/CHANGELOG.md +29 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +256 -0
  8. data/Rakefile +19 -0
  9. data/active_merchant-epsilon.gemspec +31 -0
  10. data/lib/active_merchant/billing/convenience_store.rb +55 -0
  11. data/lib/active_merchant/billing/gateways/epsilon.rb +194 -0
  12. data/lib/active_merchant/billing/gateways/epsilon/epsilon_base.rb +132 -0
  13. data/lib/active_merchant/billing/gateways/epsilon/epsilon_mission_code.rb +30 -0
  14. data/lib/active_merchant/billing/gateways/epslion_convenience_store.rb +27 -0
  15. data/lib/active_merchant/epsilon.rb +9 -0
  16. data/lib/active_merchant/epsilon/version.rb +5 -0
  17. data/test/fixtures/vcr_cassettes/autheticate_three_d_secure_card_successful.yml +46 -0
  18. data/test/fixtures/vcr_cassettes/cancel_recurring_fail.yml +82 -0
  19. data/test/fixtures/vcr_cassettes/cancel_recurring_successful.yml +83 -0
  20. data/test/fixtures/vcr_cassettes/change_recurring_amount_failure.yml +85 -0
  21. data/test/fixtures/vcr_cassettes/change_recurring_amount_successful.yml +85 -0
  22. data/test/fixtures/vcr_cassettes/convenience_store_purchase_fail.yml +39 -0
  23. data/test/fixtures/vcr_cassettes/convenience_store_purchase_successful.yml +50 -0
  24. data/test/fixtures/vcr_cassettes/find_user_failure.yml +44 -0
  25. data/test/fixtures/vcr_cassettes/find_user_success.yml +44 -0
  26. data/test/fixtures/vcr_cassettes/installment_purchase_successful.yml +46 -0
  27. data/test/fixtures/vcr_cassettes/purchase_fail.yml +45 -0
  28. data/test/fixtures/vcr_cassettes/purchase_successful.yml +46 -0
  29. data/test/fixtures/vcr_cassettes/purchase_with_three_d_secure_card_successful.yml +45 -0
  30. data/test/fixtures/vcr_cassettes/purchase_with_verification_value.yml +46 -0
  31. data/test/fixtures/vcr_cassettes/recurring_fail.yml +45 -0
  32. data/test/fixtures/vcr_cassettes/recurring_successful.yml +46 -0
  33. data/test/fixtures/vcr_cassettes/registered_purchase_fail.yml +45 -0
  34. data/test/fixtures/vcr_cassettes/registered_purchase_successful.yml +46 -0
  35. data/test/fixtures/vcr_cassettes/registered_recurring_fail.yml +45 -0
  36. data/test/fixtures/vcr_cassettes/registered_recurring_successful.yml +46 -0
  37. data/test/fixtures/vcr_cassettes/revolving_purchase_successful.yml +46 -0
  38. data/test/fixtures/vcr_cassettes/terminate_recurring_fail.yml +96 -0
  39. data/test/fixtures/vcr_cassettes/terminate_recurring_successful.yml +82 -0
  40. data/test/fixtures/vcr_cassettes/verify_fail.yml +45 -0
  41. data/test/fixtures/vcr_cassettes/verify_successful.yml +85 -0
  42. data/test/fixtures/vcr_cassettes/void_fail.yml +42 -0
  43. data/test/fixtures/vcr_cassettes/void_successful.yml +85 -0
  44. data/test/remote/gateways/remote_epsilon_convenience_store_test.rb +27 -0
  45. data/test/remote/gateways/remote_epsilon_test.rb +249 -0
  46. data/test/test_helper.rb +162 -0
  47. data/test/unit/gateways/epsilon_test.rb +11 -0
  48. data/wercker.yml +18 -0
  49. metadata +248 -0
@@ -0,0 +1,194 @@
1
+ require 'active_support/core_ext/string'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class EpsilonGateway < EpsilonBaseGateway
6
+ module CreditType
7
+ SINGLE = 10
8
+ INSTALLMENT = 61
9
+ REVOLVING = 80
10
+ end
11
+
12
+ PATHS = {
13
+ purchase: 'direct_card_payment.cgi',
14
+ registered_recurring: 'direct_card_payment.cgi',
15
+ registered_purchase: 'direct_card_payment.cgi',
16
+ cancel_recurring: 'receive_order3.cgi',
17
+ terminate_recurring: 'receive_order3.cgi',
18
+ void: 'cancel_payment.cgi',
19
+ find_user: 'get_user_info.cgi',
20
+ change_recurring_amount: 'change_amount_payment.cgi',
21
+ }.freeze
22
+
23
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
24
+
25
+ def purchase(amount, credit_card, detail = {})
26
+ detail[:mission_code] = EpsilonMissionCode::PURCHASE
27
+
28
+ params = billing_params(amount, credit_card, detail)
29
+
30
+ commit(PATHS[:purchase], params)
31
+ end
32
+
33
+ def registered_purchase(amount, detail = {})
34
+ params = {
35
+ contract_code: self.contract_code,
36
+ user_id: detail[:user_id],
37
+ user_name: detail[:user_name],
38
+ user_mail_add: detail[:user_email],
39
+ item_code: detail[:item_code],
40
+ item_name: detail[:item_name],
41
+ order_number: detail[:order_number],
42
+ st_code: '10000-0000-0000',
43
+ mission_code: EpsilonMissionCode::PURCHASE,
44
+ item_price: amount,
45
+ process_code: 2,
46
+ xml: 1,
47
+ }
48
+
49
+ commit(PATHS[:registered_purchase], params)
50
+ end
51
+
52
+ def recurring(amount, credit_card, detail = {})
53
+ detail[:mission_code] ||= EpsilonMissionCode::RECURRING_2
54
+
55
+ requires!(detail, [:mission_code, *EpsilonMissionCode::RECURRINGS])
56
+
57
+ params = billing_params(amount, credit_card, detail)
58
+
59
+ commit(PATHS[:purchase], params)
60
+ end
61
+
62
+ def registered_recurring(amount, detail = {})
63
+ params = {
64
+ contract_code: self.contract_code,
65
+ user_id: detail[:user_id],
66
+ user_name: detail[:user_name],
67
+ user_mail_add: detail[:user_email],
68
+ item_code: detail[:item_code],
69
+ item_name: detail[:item_name],
70
+ order_number: detail[:order_number],
71
+ st_code: '10000-0000-0000',
72
+ mission_code: detail[:mission_code],
73
+ item_price: amount,
74
+ process_code: 2,
75
+ xml: 1,
76
+ }
77
+
78
+ commit(PATHS[:registered_recurring], params)
79
+ end
80
+
81
+ def cancel_recurring(user_id:, item_code:)
82
+ params = {
83
+ contract_code: self.contract_code,
84
+ user_id: user_id,
85
+ item_code: item_code,
86
+ xml: 1,
87
+ process_code: 8,
88
+ }
89
+
90
+ commit(PATHS[:cancel_recurring], params)
91
+ end
92
+
93
+ def terminate_recurring(user_id:)
94
+ params = {
95
+ contract_code: self.contract_code,
96
+ user_id: user_id,
97
+ xml: 1,
98
+ process_code: 9,
99
+ }
100
+
101
+ commit(PATHS[:terminate_recurring], params)
102
+ end
103
+
104
+ def find_user(user_id:)
105
+ params = {
106
+ contract_code: self.contract_code,
107
+ user_id: user_id,
108
+ }
109
+
110
+ commit(PATHS[:find_user], params)
111
+ end
112
+
113
+ def change_recurring_amount(new_item_price:, user_id:, item_code:)
114
+ params = {
115
+ contract_code: self.contract_code,
116
+ mission_code: 2,
117
+ user_id: user_id,
118
+ item_code: item_code,
119
+ new_item_price: new_item_price,
120
+ }
121
+ commit(PATHS[:change_recurring_amount], params)
122
+ end
123
+
124
+ #
125
+ # Second request for 3D secure
126
+ #
127
+ def authenticate(order_number:, three_d_secure_pa_res:)
128
+ params = {
129
+ contract_code: self.contract_code,
130
+ order_number: order_number,
131
+ tds_check_code: 2,
132
+ tds_pares: three_d_secure_pa_res,
133
+ }
134
+
135
+ commit(PATHS[:purchase], params)
136
+ end
137
+
138
+ def void(order_number)
139
+ params = {
140
+ contract_code: self.contract_code,
141
+ order_number: order_number,
142
+ }
143
+
144
+ commit(PATHS[:void], params)
145
+ end
146
+
147
+ def verify(credit_card, options = {})
148
+ o = options.dup
149
+ o[:order_number] ||= "#{Time.now.to_i}#{options[:user_id]}".first(32)
150
+ o[:item_code] = 'verifycreditcard'
151
+ o[:item_name] = 'verify credit card'
152
+
153
+ MultiResponse.run(:use_first_response) do |r|
154
+ r.process { purchase(1, credit_card, o) }
155
+ r.process(:ignore_result) { void(o[:order_number]) }
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ def billing_params(amount, payment_method, detail)
162
+ params = {
163
+ contract_code: self.contract_code,
164
+ user_id: detail[:user_id],
165
+ user_name: detail[:user_name],
166
+ user_mail_add: detail[:user_email],
167
+ item_code: detail[:item_code],
168
+ item_name: detail[:item_name],
169
+ order_number: detail[:order_number],
170
+ st_code: '10000-0000-0000',
171
+ mission_code: detail[:mission_code],
172
+ item_price: amount,
173
+ process_code: 1,
174
+ card_number: payment_method.number,
175
+ expire_y: payment_method.year,
176
+ expire_m: payment_method.month,
177
+ card_st_code: detail[:credit_type],
178
+ pay_time: detail[:payment_time],
179
+ tds_check_code: detail[:three_d_secure_check_code],
180
+ user_agent: "#{ActiveMerchant::Epsilon}-#{ActiveMerchant::Epsilon::VERSION}",
181
+ }
182
+
183
+ if payment_method.class.requires_verification_value?
184
+ params.merge!(
185
+ security_code: payment_method.verification_value,
186
+ security_check: 1, # use security code
187
+ )
188
+ end
189
+
190
+ params
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,132 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc
5
+
6
+ #
7
+ # This is the base gateway for concrete payment gateway for Epsilon
8
+ #
9
+ # === Create a new EpsilonGatway
10
+ #
11
+ # class EpsilonSampleGateway < EpsilonBaseGateway
12
+ # def purchase(amount, payment_method, detail = {})
13
+ # request_params = {
14
+ # contract_code: self.contract_code,
15
+ # user_id: detail[:user_id],
16
+ # item_code: detail[:item_code],
17
+ # order_number: detail[:order_number],
18
+ # item_price: amount,
19
+ # }
20
+ #
21
+ # request_path = 'purchase'
22
+ #
23
+ # commit(request_path, request_paramsparams)
24
+ # end
25
+ # end
26
+ #
27
+
28
+ class EpsilonBaseGateway < Gateway
29
+
30
+ self.abstract_class = true
31
+
32
+ module ResponseXpath
33
+ RESULT = '//Epsilon_result/result[@result]/@result'
34
+ TRANSACTION_CODE = '//Epsilon_result/result[@trans_code]/@trans_code'
35
+ ERROR_CODE = '//Epsilon_result/result[@err_code]/@err_code'
36
+ ERROR_DETAIL = '//Epsilon_result/result[@err_detail]/@err_detail'
37
+ CARD_NUMBER_MASK = '//Epsilon_result/result[@card_number_mask]/@card_number_mask'
38
+ CARD_BRAND = '//Epsilon_result/result[@card_brand]/@card_brand'
39
+ ACS_URL = '//Epsilon_result/result[@acsurl]/@acsurl' # ACS (Access Control Server)
40
+ PA_REQ = '//Epsilon_result/result[@pareq]/@pareq' # PAReq (payer authentication request)
41
+ RECEIPT_NUMBER = '//Epsilon_result/result[@receipt_no][1]/@receipt_no'
42
+ RECEIPT_DATE = '//Epsilon_result/result[@receipt_date][1]/@receipt_date'
43
+ CONVENIENCE_STORE_LIMIT_DATE = '//Epsilon_result/result[@conveni_limit][1]/@conveni_limit'
44
+ end
45
+
46
+ module ResultCode
47
+ FAILURE = '0'
48
+ SUCCESS = '1'
49
+ THREE_D_SECURE = '5'
50
+ SYSTEM_ERROR = '9'
51
+ end
52
+
53
+ cattr_accessor :contract_code, :proxy_address, :proxy_port
54
+
55
+ self.test_url = 'https://beta.epsilon.jp/cgi-bin/order/'
56
+ self.live_url = 'https://secure.epsilon.jp/cgi-bin/order/'
57
+ self.supported_countries = ['JP']
58
+ self.default_currency = 'JPY'
59
+ self.homepage_url = 'http://www.example.net/'
60
+ self.display_name = 'New Gateway'
61
+
62
+ private
63
+
64
+ def authorization_from(response)
65
+ {}
66
+ end
67
+
68
+ def commit(path, params)
69
+ url = (test? ? test_url : live_url)
70
+ raw_response = ssl_post(File.join(url, path), post_data(params))
71
+ response = parse(raw_response)
72
+
73
+ options = {
74
+ authorization: authorization_from(response),
75
+ test: test?
76
+ }
77
+
78
+ Response.new(success_from(response), message_from(response), response, options)
79
+ end
80
+
81
+ def message_from(response)
82
+ response[:message]
83
+ end
84
+
85
+ def parse(body)
86
+ # because of following error
87
+ # Nokogiri::XML::SyntaxError: Unsupported encoding x-sjis-cp932
88
+ xml = Nokogiri::XML(body.sub('x-sjis-cp932', 'UTF-8'))
89
+
90
+ result = xml.xpath(ResponseXpath::RESULT).to_s
91
+ transaction_code = xml.xpath(ResponseXpath::TRANSACTION_CODE).to_s
92
+ error_code = xml.xpath(ResponseXpath::ERROR_CODE).to_s
93
+ error_detail = uri_decode(xml.xpath(ResponseXpath::ERROR_DETAIL).to_s)
94
+ card_number_mask = uri_decode(xml.xpath(ResponseXpath::CARD_NUMBER_MASK).to_s)
95
+ card_brand = uri_decode(xml.xpath(ResponseXpath::CARD_BRAND).to_s)
96
+ acs_url = uri_decode(xml.xpath(ResponseXpath::ACS_URL).to_s)
97
+ pa_req = uri_decode(xml.xpath(ResponseXpath::PA_REQ).to_s)
98
+ receipt_number = xml.xpath(ResponseXpath::RECEIPT_NUMBER).to_s
99
+ receipt_date = uri_decode(xml.xpath(ResponseXpath::RECEIPT_DATE).to_s)
100
+ convenience_store_limit_date = uri_decode(xml.xpath(ResponseXpath::CONVENIENCE_STORE_LIMIT_DATE).to_s)
101
+
102
+ {
103
+ success: [ResultCode::SUCCESS, ResultCode::THREE_D_SECURE].include?(result),
104
+ message: "#{error_code}: #{error_detail}",
105
+ transaction_code: transaction_code,
106
+ error_code: error_code,
107
+ error_detail: error_detail,
108
+ card_number_mask: card_number_mask,
109
+ card_brand: card_brand,
110
+ three_d_secure: result == ResultCode::THREE_D_SECURE,
111
+ acs_url: acs_url,
112
+ pa_req: pa_req,
113
+ receipt_number: receipt_number,
114
+ receipt_date: receipt_date,
115
+ convenience_store_limit_date: convenience_store_limit_date,
116
+ }
117
+ end
118
+
119
+ def post_data(parameters = {})
120
+ parameters.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
121
+ end
122
+
123
+ def success_from(response)
124
+ response[:success]
125
+ end
126
+
127
+ def uri_decode(string)
128
+ URI.decode(string).encode(Encoding::UTF_8, Encoding::CP932)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module EpsilonMissionCode
4
+ # クレジット1回、またはクレジット決済以外の場合
5
+ PURCHASE = 1
6
+
7
+ # | CODE | 登録月 | 解除月 | 同月内での登録解除 |
8
+ # | 2 | 全額 | 無料 | 1ヶ月分 |
9
+ # | 3 | 全額 | 全額 | 1ヶ月分 |
10
+ # | 4 | 全額 | 日割 | 1ヶ月分 |
11
+ # | 5 | 無料 | 無料 | 無料 |
12
+ # | 6 | 無料 | 全角 | 1ヶ月分 |
13
+ # | 7 | 無料 | 日割 | 日割 |
14
+ # | 8 | 日割 | 無料 | 日割 |
15
+ # | 9 | 日割 | 全額 | 1ヶ月分 |
16
+ # | 10 | 日割 | 日割 | 日割 |
17
+ RECURRING_2 = 2
18
+ RECURRING_3 = 3
19
+ RECURRING_4 = 4
20
+ RECURRING_5 = 5
21
+ RECURRING_6 = 6
22
+ RECURRING_7 = 7
23
+ RECURRING_8 = 8
24
+ RECURRING_9 = 9
25
+ RECURRING_10 = 10
26
+
27
+ RECURRINGS = (2..10).to_a.freeze
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class EpsilonConvenienceStoreGateway < EpsilonBaseGateway
4
+ def purchase(amount, payment_method, detail = {})
5
+ params = {
6
+ contract_code: self.contract_code,
7
+ user_id: detail[:user_id],
8
+ user_name: detail[:user_name],
9
+ user_mail_add: detail[:user_email],
10
+ item_code: detail[:item_code],
11
+ item_name: detail[:item_name],
12
+ order_number: detail[:order_number],
13
+ st_code: '00100-0000-0000',
14
+ mission_code: EpsilonMissionCode::PURCHASE,
15
+ item_price: amount,
16
+ process_code: 1,
17
+ xml: 1,
18
+ conveni_code: payment_method.code,
19
+ user_tel: payment_method.phone_number,
20
+ user_name_kana: payment_method.name,
21
+ }
22
+
23
+ commit('receive_order3.cgi', params)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_merchant'
2
+
3
+ require_relative 'epsilon/version'
4
+
5
+ require_relative 'billing/convenience_store'
6
+ require_relative 'billing/gateways/epsilon/epsilon_mission_code'
7
+ require_relative 'billing/gateways/epsilon/epsilon_base'
8
+ require_relative 'billing/gateways/epsilon'
9
+ require_relative 'billing/gateways/epslion_convenience_store'
@@ -0,0 +1,5 @@
1
+ module ActiveMerchant
2
+ module Epsilon
3
+ VERSION = "0.5.1"
4
+ end
5
+ end
@@ -0,0 +1,46 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://beta.epsilon.jp/cgi-bin/order/direct_card_payment.cgi
6
+ body:
7
+ encoding: US-ASCII
8
+ string: contract_code=[CONTRACT_CODE]&order_number=O42938159&tds_check_code=2&tds_pares=xxxxxxxxxxxxxxxx
9
+ headers:
10
+ Content-Type:
11
+ - application/x-www-form-urlencoded
12
+ Accept-Encoding:
13
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
+ Accept:
15
+ - "*/*"
16
+ User-Agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Tue, 31 Mar 2015 03:58:45 GMT
25
+ Server:
26
+ - Apache
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Content-Type:
30
+ - text/xml; charset=CP932
31
+ body:
32
+ encoding: UTF-8
33
+ string: |-
34
+ <?xml version="1.0" encoding="x-sjis-cp932"?>
35
+ <Epsilon_result>
36
+ <result acsurl="" />
37
+ <result err_code="" />
38
+ <result err_detail="" />
39
+ <result kari_flag="0" />
40
+ <result pareq="" />
41
+ <result result="1" />
42
+ <result trans_code="521710" />
43
+ </Epsilon_result>
44
+ http_version:
45
+ recorded_at: Tue, 31 Mar 2015 03:58:43 GMT
46
+ recorded_with: VCR 2.9.3