killbill-orbital 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/NEWS +5 -0
- data/VERSION +1 -1
- data/lib/orbital/ext/active_merchant/active_merchant.rb +103 -3
- data/orbital.yml +1 -1
- data/pom.xml +1 -1
- data/spec/orbital/cvv_indicator_spec.rb +171 -0
- data/spec/orbital/remote/integration_spec.rb +53 -256
- data/spec/orbital/remote/shared_examples_for_payment_flow.rb +120 -0
- data/spec/orbital/tokenized_creditcard_payload_spec.rb +134 -0
- data/spec/spec_helper.rb +8 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: caf9e96477c8998f8775bafa6ac5880e9864ff52
|
4
|
+
data.tar.gz: a2db465f2f8dbbba3f2f348ad04946147fe62acd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63322da8cd5cd93f014c19a8f160641f34847a22ceac9cb8dabf06efb25353755cf75839ed8a6aacc542b69dca4b2b85e00209fbd5dc8d1baba3469642808064
|
7
|
+
data.tar.gz: 50548d3981b5fba7f051c1feda33ff772666826392967265a79bead2ef987eaf02102599cda739450c2cbde5868d30631c17adb9fa71ab4e206227e6e93bbe76
|
data/Gemfile.lock
CHANGED
data/NEWS
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
@@ -5,6 +5,8 @@ module ActiveMerchant
|
|
5
5
|
|
6
6
|
class OrbitalGateway
|
7
7
|
|
8
|
+
API_VERSION = '7.0.1'
|
9
|
+
|
8
10
|
def store(creditcard, options = {})
|
9
11
|
response = add_customer_profile(creditcard, options)
|
10
12
|
|
@@ -36,7 +38,8 @@ module ActiveMerchant
|
|
36
38
|
|
37
39
|
headers = POST_HEADERS.merge('Content-length' => order.size.to_s,
|
38
40
|
'User-Agent' => user_agent,
|
39
|
-
'Interface-Version' => 'Ruby|KillBill|Open-Source Gateway'
|
41
|
+
'Interface-Version' => 'Ruby|KillBill|Open-Source Gateway',
|
42
|
+
'Content-Type' => 'application/PTI70')
|
40
43
|
headers['X-Request-Id'] = x_r_id unless x_r_id.blank?
|
41
44
|
headers.merge!('Trace-number' => trace_number.to_s,
|
42
45
|
'Merchant-Id' => @options[:merchant_id]) if @options[:retry_logic] && trace_number
|
@@ -60,32 +63,94 @@ module ActiveMerchant
|
|
60
63
|
})
|
61
64
|
end
|
62
65
|
|
66
|
+
def build_new_order_xml(action, money, parameters = {})
|
67
|
+
requires!(parameters, :order_id)
|
68
|
+
xml = xml_envelope
|
69
|
+
xml.tag! :Request do
|
70
|
+
xml.tag! :NewOrder do
|
71
|
+
add_xml_credentials(xml)
|
72
|
+
# EC - Ecommerce transaction
|
73
|
+
# RC - Recurring Payment transaction
|
74
|
+
# MO - Mail Order Telephone Order transaction
|
75
|
+
# IV - Interactive Voice Response
|
76
|
+
# IN - Interactive Voice Response
|
77
|
+
xml.tag! :IndustryType, parameters[:industry_type] || ECOMMERCE_TRANSACTION
|
78
|
+
# A - Auth Only No Capture
|
79
|
+
# AC - Auth and Capture
|
80
|
+
# F - Force Auth No Capture and no online authorization
|
81
|
+
# FR - Force Auth No Capture and no online authorization
|
82
|
+
# FC - Force Auth and Capture no online authorization
|
83
|
+
# R - Refund and Capture no online authorization
|
84
|
+
xml.tag! :MessageType, action
|
85
|
+
add_bin_merchant_and_terminal(xml, parameters)
|
86
|
+
|
87
|
+
yield xml if block_given?
|
88
|
+
|
89
|
+
xml.tag! :OrderID, format_order_id(parameters[:order_id])
|
90
|
+
xml.tag! :Amount, amount(money)
|
91
|
+
xml.tag! :Comments, parameters[:comments] if parameters[:comments]
|
92
|
+
|
93
|
+
# Add additional card information for tokenized credit card that must be placed after the above three elements
|
94
|
+
if action == AUTH_ONLY || action == AUTH_AND_CAPTURE
|
95
|
+
add_additional_network_tokenization(xml, parameters[:creditcard]) unless parameters[:creditcard].nil?
|
96
|
+
end
|
97
|
+
|
98
|
+
if parameters[:soft_descriptors].is_a?(OrbitalSoftDescriptors)
|
99
|
+
add_soft_descriptors(xml, parameters[:soft_descriptors])
|
100
|
+
end
|
101
|
+
|
102
|
+
set_recurring_ind(xml, parameters)
|
103
|
+
|
104
|
+
# Append Transaction Reference Number at the end for Refund transactions
|
105
|
+
if action == REFUND && !parameters[:authorization].nil?
|
106
|
+
tx_ref_num, _ = split_authorization(parameters[:authorization])
|
107
|
+
xml.tag! :TxRefNum, tx_ref_num
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
xml.target!
|
112
|
+
end
|
113
|
+
|
63
114
|
# A – Authorization request
|
64
115
|
def authorize(money, creditcard, options = {})
|
65
|
-
order = build_new_order_xml(AUTH_ONLY, money, options) do |xml|
|
116
|
+
order = build_new_order_xml(AUTH_ONLY, money, options.merge(:creditcard=>creditcard)) do |xml|
|
66
117
|
add_creditcard(xml, creditcard, options)
|
67
118
|
add_address(xml, creditcard, options)
|
68
119
|
if @options[:customer_profiles]
|
69
120
|
add_customer_data(xml, creditcard, options)
|
70
121
|
add_managed_billing(xml, options)
|
71
122
|
end
|
123
|
+
add_network_tokenization(xml, creditcard)
|
72
124
|
end
|
73
125
|
commit(order, :authorize, options[:trace_number])
|
74
126
|
end
|
75
127
|
|
76
128
|
# AC – Authorization and Capture
|
77
129
|
def purchase(money, creditcard, options = {})
|
78
|
-
order = build_new_order_xml(AUTH_AND_CAPTURE, money, options) do |xml|
|
130
|
+
order = build_new_order_xml(AUTH_AND_CAPTURE, money, options.merge(:creditcard=>creditcard)) do |xml|
|
79
131
|
add_creditcard(xml, creditcard, options)
|
80
132
|
add_address(xml, creditcard, options)
|
81
133
|
if @options[:customer_profiles]
|
82
134
|
add_customer_data(xml, creditcard, options)
|
83
135
|
add_managed_billing(xml, options)
|
84
136
|
end
|
137
|
+
add_network_tokenization(xml, creditcard)
|
85
138
|
end
|
86
139
|
commit(order, :purchase, options[:trace_number])
|
87
140
|
end
|
88
141
|
|
142
|
+
def credit(money, creditcard, options= {})
|
143
|
+
order = build_new_order_xml(REFUND, money, options) do |xml|
|
144
|
+
add_creditcard(xml, creditcard, options)
|
145
|
+
add_address(xml, creditcard, options)
|
146
|
+
if @options[:customer_profiles]
|
147
|
+
add_customer_data(xml, creditcard, options)
|
148
|
+
add_managed_billing(xml, options)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
commit(order, :credit, options[:trace_number])
|
152
|
+
end
|
153
|
+
|
89
154
|
def add_creditcard(xml, creditcard, options = {})
|
90
155
|
currency = options[:currency]
|
91
156
|
cvv_indicator_visa_discover = options[:cvv_indicator_visa_discover]
|
@@ -111,6 +176,41 @@ module ActiveMerchant
|
|
111
176
|
end
|
112
177
|
end
|
113
178
|
|
179
|
+
def add_network_tokenization(xml, payment_method)
|
180
|
+
return unless network_tokenization?(payment_method)
|
181
|
+
card_brand = card_brand(payment_method).to_sym
|
182
|
+
|
183
|
+
# The elements must follow a specific sequence
|
184
|
+
xml.tag!('AuthenticationECIInd', payment_method.eci) unless payment_method.eci.nil?
|
185
|
+
xml.tag!('CAVV', payment_method.payment_cryptogram) if card_brand == :visa
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_additional_network_tokenization(xml, payment_method)
|
189
|
+
return unless network_tokenization?(payment_method)
|
190
|
+
card_brand = card_brand(payment_method).to_sym
|
191
|
+
|
192
|
+
# The elements must follow a specific sequence
|
193
|
+
xml.tag!('AAV', payment_method.payment_cryptogram) if card_brand == :master
|
194
|
+
xml.tag!('DPANInd', 'Y')
|
195
|
+
xml.tag!('AEVV', payment_method.payment_cryptogram) if card_brand == :american_express
|
196
|
+
xml.tag!('DigitalTokenCryptogram', payment_method.payment_cryptogram)
|
197
|
+
end
|
198
|
+
|
199
|
+
def network_tokenization?(payment_method)
|
200
|
+
payment_method.is_a?(NetworkTokenizationCreditCard)
|
201
|
+
end
|
202
|
+
|
203
|
+
def success?(response, message_type)
|
204
|
+
if [:refund, :void, :credit].include?(message_type)
|
205
|
+
response[:proc_status] == SUCCESS
|
206
|
+
elsif response[:customer_profile_action]
|
207
|
+
response[:profile_proc_status] == SUCCESS
|
208
|
+
else
|
209
|
+
response[:proc_status] == SUCCESS &&
|
210
|
+
APPROVED.include?(response[:resp_code])
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
114
214
|
end
|
115
215
|
end
|
116
216
|
end
|
data/orbital.yml
CHANGED
data/pom.xml
CHANGED
@@ -25,7 +25,7 @@
|
|
25
25
|
<groupId>org.kill-bill.billing.plugin.ruby</groupId>
|
26
26
|
<artifactId>orbital-plugin</artifactId>
|
27
27
|
<packaging>pom</packaging>
|
28
|
-
<version>0.1.
|
28
|
+
<version>0.1.2</version>
|
29
29
|
<name>orbital-plugin</name>
|
30
30
|
<url>http://github.com/killbill/killbill-orbital-plugin</url>
|
31
31
|
<description>Plugin for accessing Orbital as a payment gateway</description>
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
ActiveMerchant::Billing::Base.mode = :test
|
4
|
+
|
5
|
+
describe Killbill::Orbital::PaymentPlugin do
|
6
|
+
|
7
|
+
include ::Killbill::Plugin::ActiveMerchant::RSpec
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@plugin = build_plugin(::Killbill::Orbital::PaymentPlugin, 'orbital')
|
11
|
+
@plugin.start_plugin
|
12
|
+
@call_context = build_call_context
|
13
|
+
|
14
|
+
::Killbill::Orbital::OrbitalPaymentMethod.delete_all
|
15
|
+
::Killbill::Orbital::OrbitalResponse.delete_all
|
16
|
+
::Killbill::Orbital::OrbitalTransaction.delete_all
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:each) do
|
20
|
+
@plugin.stop_plugin
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'cvv indicator spec' do
|
24
|
+
before(:each) do
|
25
|
+
@amount = BigDecimal.new('100')
|
26
|
+
@currency = 'USD'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should set correct indicator for visa and discover if cvv value is present regardless of cvv_indicator_visa_discover' do
|
30
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454' })
|
31
|
+
validate_cvv_indicator_field 1
|
32
|
+
|
33
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover' })
|
34
|
+
validate_cvv_indicator_field 1
|
35
|
+
|
36
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => true })
|
37
|
+
validate_cvv_indicator_field 1
|
38
|
+
|
39
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => true })
|
40
|
+
validate_cvv_indicator_field 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should set correct indicator for visa and discover if cvv value is not present and cvv_indicator_visa_discover is true' do
|
44
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => true })
|
45
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
46
|
+
validate_cvv_indicator_field 9
|
47
|
+
|
48
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => true })
|
49
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
50
|
+
validate_cvv_indicator_field 9
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should set customized indicator for visa and discover if cvv value is not present and cvv_indicator_visa_discover is true and cvv_indicator_override_visa_discover is given' do
|
54
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => true, :cvv_indicator_override_visa_discover => '2' })
|
55
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
56
|
+
validate_cvv_indicator_field 2
|
57
|
+
|
58
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => true, :cvv_indicator_override_visa_discover => '2' })
|
59
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
60
|
+
validate_cvv_indicator_field 2
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should set correct indicator for visa and discover if cvv value is not present and cvv_indicator_visa_discover is nil or false' do
|
64
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454'})
|
65
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
66
|
+
validate_cvv_indicator_field
|
67
|
+
|
68
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover'})
|
69
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
70
|
+
validate_cvv_indicator_field
|
71
|
+
|
72
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => false })
|
73
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
74
|
+
validate_cvv_indicator_field
|
75
|
+
|
76
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => false })
|
77
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
78
|
+
validate_cvv_indicator_field
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should not include indicator except visa and discover for all cases' do
|
82
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master' })
|
83
|
+
validate_cvv_indicator_field
|
84
|
+
|
85
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express' })
|
86
|
+
validate_cvv_indicator_field
|
87
|
+
|
88
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => false })
|
89
|
+
validate_cvv_indicator_field
|
90
|
+
|
91
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => false })
|
92
|
+
validate_cvv_indicator_field
|
93
|
+
|
94
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => true})
|
95
|
+
validate_cvv_indicator_field
|
96
|
+
|
97
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => true })
|
98
|
+
validate_cvv_indicator_field
|
99
|
+
|
100
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master' })
|
101
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
102
|
+
validate_cvv_indicator_field
|
103
|
+
|
104
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express' })
|
105
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
106
|
+
validate_cvv_indicator_field
|
107
|
+
|
108
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => false})
|
109
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
110
|
+
validate_cvv_indicator_field
|
111
|
+
|
112
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => false })
|
113
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
114
|
+
validate_cvv_indicator_field
|
115
|
+
|
116
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => true})
|
117
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
118
|
+
validate_cvv_indicator_field
|
119
|
+
|
120
|
+
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => true })
|
121
|
+
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
122
|
+
validate_cvv_indicator_field
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def successful_authorize_response
|
127
|
+
<<-XML
|
128
|
+
<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><NewOrderResp><IndustryType></IndustryType><MessageType>A</MessageType><MerchantID>1111111</MerchantID><TerminalID>001</TerminalID><CardBrand>CC</CardBrand><AccountNum>XXXXXXXXXXXX5454</AccountNum><OrderID>5b257b31-1f84-44bc-b32</OrderID><TxRefNum>5834AA75E4466AEA59512165057C37DD810053C2</TxRefNum><TxRefIdx>0</TxRefIdx><ProcStatus>0</ProcStatus><ApprovalStatus>1</ApprovalStatus><RespCode>00</RespCode><AVSRespCode>B </AVSRespCode><CVV2RespCode> </CVV2RespCode><AuthCode>tst424</AuthCode><RecurringAdviceCd></RecurringAdviceCd><CAVVRespCode></CAVVRespCode><StatusMsg>Approved</StatusMsg><RespMsg></RespMsg><HostRespCode>100</HostRespCode><HostAVSRespCode>I3</HostAVSRespCode><HostCVV2RespCode> </HostCVV2RespCode><CustomerRefNum></CustomerRefNum><CustomerName></CustomerName><ProfileProcStatus></ProfileProcStatus><CustomerProfileMessage></CustomerProfileMessage><RespTime>152837</RespTime><PartialAuthOccurred></PartialAuthOccurred><RequestedAmount></RequestedAmount><RedeemedAmount></RedeemedAmount><RemainingBalance></RemainingBalance><CountryFraudFilterStatus></CountryFraudFilterStatus><IsoCountryCode></IsoCountryCode></NewOrderResp></Response>
|
129
|
+
XML
|
130
|
+
end
|
131
|
+
|
132
|
+
def successful_purchase_response
|
133
|
+
<<-XML
|
134
|
+
<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><NewOrderResp><IndustryType></IndustryType><MessageType>AC</MessageType><MerchantID>1111111</MerchantID><TerminalID>001</TerminalID><CardBrand>CC</CardBrand><AccountNum>XXXXXXXXXXXX5454</AccountNum><OrderID>88132d30-f4f7-4028-949</OrderID><TxRefNum>5834EAC9C7A53FEB600A479629FB6C6427A2532C</TxRefNum><TxRefIdx>1</TxRefIdx><ProcStatus>0</ProcStatus><ApprovalStatus>1</ApprovalStatus><RespCode>00</RespCode><AVSRespCode>3 </AVSRespCode><CVV2RespCode>I</CVV2RespCode><AuthCode>tst703</AuthCode><RecurringAdviceCd></RecurringAdviceCd><CAVVRespCode></CAVVRespCode><StatusMsg>Approved</StatusMsg><RespMsg></RespMsg><HostRespCode>100</HostRespCode><HostAVSRespCode> </HostAVSRespCode><HostCVV2RespCode>I</HostCVV2RespCode><CustomerRefNum></CustomerRefNum><CustomerName></CustomerName><ProfileProcStatus></ProfileProcStatus><CustomerProfileMessage></CustomerProfileMessage><RespTime>200306</RespTime><PartialAuthOccurred></PartialAuthOccurred><RequestedAmount></RequestedAmount><RedeemedAmount></RedeemedAmount><RemainingBalance></RemainingBalance><CountryFraudFilterStatus></CountryFraudFilterStatus><IsoCountryCode></IsoCountryCode></NewOrderResp></Response> XML
|
135
|
+
XML
|
136
|
+
end
|
137
|
+
|
138
|
+
def validate_cvv_indicator_field(expected_field = nil)
|
139
|
+
::ActiveMerchant::Billing::OrbitalGateway.any_instance.stub(:ssl_post) do |host, request_body|
|
140
|
+
if expected_field.nil?
|
141
|
+
request_body.should_not match('<CardSecValInd>')
|
142
|
+
else
|
143
|
+
request_body.should match("<CardSecValInd>#{expected_field}</CardSecValInd>")
|
144
|
+
end
|
145
|
+
if request_body.include? '<MessageType>A</MessageType>'
|
146
|
+
successful_authorize_response
|
147
|
+
else
|
148
|
+
successful_purchase_response
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
authorize
|
153
|
+
purchase
|
154
|
+
end
|
155
|
+
|
156
|
+
def authorize
|
157
|
+
kb_payment_id, kb_transaction_id = create_payment
|
158
|
+
payment_response = @plugin.authorize_payment(SecureRandom.uuid, kb_payment_id, kb_transaction_id, SecureRandom.uuid, @amount, @currency, @properties, @call_context)
|
159
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
160
|
+
payment_response.amount.should == @amount
|
161
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
162
|
+
end
|
163
|
+
|
164
|
+
def purchase
|
165
|
+
kb_payment_id, kb_transaction_id = create_payment
|
166
|
+
payment_response = @plugin.purchase_payment(SecureRandom.uuid, kb_payment_id, kb_transaction_id, SecureRandom.uuid, @amount, @currency, @properties, @call_context)
|
167
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
168
|
+
payment_response.amount.should == @amount
|
169
|
+
payment_response.transaction_type.should == :PURCHASE
|
170
|
+
end
|
171
|
+
end
|
@@ -1,222 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require_relative 'shared_examples_for_payment_flow'
|
2
3
|
|
3
4
|
ActiveMerchant::Billing::Base.mode = :test
|
4
5
|
|
5
|
-
shared_examples 'common_specs' do
|
6
|
-
before(:each) do
|
7
|
-
create_payment
|
8
|
-
end
|
9
|
-
|
10
|
-
after(:each) do
|
11
|
-
@plugin.stop_plugin
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'should be able to purchase' do
|
15
|
-
Killbill::Orbital::OrbitalResponse.all.size.should == 1
|
16
|
-
Killbill::Orbital::OrbitalTransaction.all.size.should == 0
|
17
|
-
|
18
|
-
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
19
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
20
|
-
payment_response.amount.should == @amount
|
21
|
-
payment_response.transaction_type.should == :PURCHASE
|
22
|
-
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
23
|
-
|
24
|
-
responses = Killbill::Orbital::OrbitalResponse.all
|
25
|
-
responses.size.should == 2
|
26
|
-
responses[0].api_call.should == 'add_payment_method'
|
27
|
-
responses[0].message.should == 'Profile Request Processed'
|
28
|
-
responses[1].api_call.should == 'purchase'
|
29
|
-
responses[1].message.should == 'Approved'
|
30
|
-
transactions = Killbill::Orbital::OrbitalTransaction.all
|
31
|
-
transactions.size.should == 1
|
32
|
-
transactions[0].api_call.should == 'purchase'
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'should be able to charge and refund' do
|
36
|
-
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
37
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
38
|
-
payment_response.amount.should == @amount
|
39
|
-
payment_response.transaction_type.should == :PURCHASE
|
40
|
-
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
41
|
-
|
42
|
-
# Try a full refund
|
43
|
-
refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
44
|
-
refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
|
45
|
-
refund_response.amount.should == @amount
|
46
|
-
refund_response.transaction_type.should == :REFUND
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'should be able to auth, capture and refund' do
|
50
|
-
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
51
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
52
|
-
payment_response.amount.should == @amount
|
53
|
-
payment_response.transaction_type.should == :AUTHORIZE
|
54
|
-
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
55
|
-
|
56
|
-
# Try multiple partial captures
|
57
|
-
partial_capture_amount = BigDecimal.new('10')
|
58
|
-
1.upto(3) do |i|
|
59
|
-
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[i].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
60
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
61
|
-
payment_response.amount.should == partial_capture_amount
|
62
|
-
payment_response.transaction_type.should == :CAPTURE
|
63
|
-
end
|
64
|
-
|
65
|
-
# Try a partial refund
|
66
|
-
refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[4].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
67
|
-
refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
|
68
|
-
refund_response.amount.should == partial_capture_amount
|
69
|
-
refund_response.transaction_type.should == :REFUND
|
70
|
-
|
71
|
-
# Try to capture again
|
72
|
-
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[5].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
73
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
74
|
-
payment_response.amount.should == partial_capture_amount
|
75
|
-
payment_response.transaction_type.should == :CAPTURE
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'should be able to auth and void' do
|
79
|
-
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
80
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
81
|
-
payment_response.amount.should == @amount
|
82
|
-
payment_response.transaction_type.should == :AUTHORIZE
|
83
|
-
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
84
|
-
|
85
|
-
payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @properties, @call_context)
|
86
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
87
|
-
payment_response.transaction_type.should == :VOID
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'should be able to auth, partial capture and void' do
|
91
|
-
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
92
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
93
|
-
payment_response.amount.should == @amount
|
94
|
-
payment_response.transaction_type.should == :AUTHORIZE
|
95
|
-
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
96
|
-
|
97
|
-
partial_capture_amount = BigDecimal.new('10')
|
98
|
-
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
99
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
100
|
-
payment_response.amount.should == partial_capture_amount
|
101
|
-
payment_response.transaction_type.should == :CAPTURE
|
102
|
-
|
103
|
-
payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[2].id, @pm.kb_payment_method_id, @properties, @call_context)
|
104
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
105
|
-
payment_response.transaction_type.should == :VOID
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'should include host response code' do
|
109
|
-
# Sending a specific amount of 530 will trigger the Do Not Honor error.
|
110
|
-
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, BigDecimal.new('530'), @currency, @properties, @call_context)
|
111
|
-
payment_response.status.should eq(:ERROR), payment_response.gateway_error
|
112
|
-
payment_response.transaction_type.should == :AUTHORIZE
|
113
|
-
payment_response.amount.should be_nil
|
114
|
-
find_value_from_properties(payment_response.properties, 'processorResponse').should == '530'
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
shared_examples 'cvv_indicator_specs' do
|
119
|
-
after(:each) do
|
120
|
-
@plugin.stop_plugin
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'should set correct indicator for visa and discover if cvv value is present regardless of cvv_indicator_visa_discover' do
|
124
|
-
validate_cvv_indicator_field 1
|
125
|
-
|
126
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover' })
|
127
|
-
validate_cvv_indicator_field 1
|
128
|
-
|
129
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => true })
|
130
|
-
validate_cvv_indicator_field 1
|
131
|
-
|
132
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => true })
|
133
|
-
validate_cvv_indicator_field 1
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'should set correct indicator for visa and discover if cvv value is not present and cvv_indicator_visa_discover is true' do
|
137
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => true })
|
138
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
139
|
-
validate_cvv_indicator_field 9
|
140
|
-
|
141
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => true })
|
142
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
143
|
-
validate_cvv_indicator_field 9
|
144
|
-
end
|
145
|
-
|
146
|
-
it 'should set customized indicator for visa and discover if cvv value is not present and cvv_indicator_visa_discover is true and cvv_indicator_override_visa_discover is given' do
|
147
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => true, :cvv_indicator_override_visa_discover => '2' })
|
148
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
149
|
-
validate_cvv_indicator_field 2
|
150
|
-
|
151
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => true, :cvv_indicator_override_visa_discover => '2' })
|
152
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
153
|
-
validate_cvv_indicator_field 2
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'should set correct indicator for visa and discover if cvv value is not present and cvv_indicator_visa_discover is nil or false' do
|
157
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454'})
|
158
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
159
|
-
validate_cvv_indicator_field
|
160
|
-
|
161
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover'})
|
162
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
163
|
-
validate_cvv_indicator_field
|
164
|
-
|
165
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cvv_indicator_visa_discover => false })
|
166
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
167
|
-
validate_cvv_indicator_field
|
168
|
-
|
169
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'discover', :cvv_indicator_visa_discover => false })
|
170
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
171
|
-
validate_cvv_indicator_field
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'should not include indicator except visa and discover for all cases' do
|
175
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master' })
|
176
|
-
validate_cvv_indicator_field
|
177
|
-
|
178
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express' })
|
179
|
-
validate_cvv_indicator_field
|
180
|
-
|
181
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => false })
|
182
|
-
validate_cvv_indicator_field
|
183
|
-
|
184
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => false })
|
185
|
-
validate_cvv_indicator_field
|
186
|
-
|
187
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => true})
|
188
|
-
validate_cvv_indicator_field
|
189
|
-
|
190
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => true })
|
191
|
-
validate_cvv_indicator_field
|
192
|
-
|
193
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master' })
|
194
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
195
|
-
validate_cvv_indicator_field
|
196
|
-
|
197
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express' })
|
198
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
199
|
-
validate_cvv_indicator_field
|
200
|
-
|
201
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => false})
|
202
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
203
|
-
validate_cvv_indicator_field
|
204
|
-
|
205
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => false })
|
206
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
207
|
-
validate_cvv_indicator_field
|
208
|
-
|
209
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'master', :cvv_indicator_visa_discover => true})
|
210
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
211
|
-
validate_cvv_indicator_field
|
212
|
-
|
213
|
-
@properties = build_pm_properties(nil, { :cc_number => '5454545454545454', :cc_type => 'american_express', :cvv_indicator_visa_discover => true })
|
214
|
-
@properties.reject! {|property| property.key == 'ccVerificationValue' }
|
215
|
-
validate_cvv_indicator_field
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
|
220
6
|
describe Killbill::Orbital::PaymentPlugin do
|
221
7
|
|
222
8
|
include ::Killbill::Plugin::ActiveMerchant::RSpec
|
@@ -241,7 +27,7 @@ describe Killbill::Orbital::PaymentPlugin do
|
|
241
27
|
@currency = 'USD'
|
242
28
|
end
|
243
29
|
|
244
|
-
include_examples '
|
30
|
+
include_examples 'payment_flow_spec'
|
245
31
|
end
|
246
32
|
|
247
33
|
context 'custom profile flow' do
|
@@ -252,63 +38,74 @@ describe Killbill::Orbital::PaymentPlugin do
|
|
252
38
|
@currency = 'USD'
|
253
39
|
end
|
254
40
|
|
255
|
-
include_examples '
|
41
|
+
include_examples 'payment_flow_spec'
|
256
42
|
end
|
257
43
|
|
258
|
-
context '
|
44
|
+
context 'tokenized credit card flow amex' do
|
259
45
|
before(:each) do
|
260
|
-
|
46
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
47
|
+
@properties = build_pm_properties(nil,
|
48
|
+
{
|
49
|
+
:cc_number => 378282246310005,
|
50
|
+
:cc_type => 'american_express',
|
51
|
+
:payment_cryptogram => cryptogram
|
52
|
+
})
|
261
53
|
@pm = create_payment_method(::Killbill::Orbital::OrbitalPaymentMethod, nil, @call_context.tenant_id, @properties, {})
|
262
54
|
@amount = BigDecimal.new('100')
|
263
55
|
@currency = 'USD'
|
264
56
|
end
|
265
57
|
|
266
|
-
include_examples '
|
58
|
+
include_examples 'payment_flow_spec'
|
267
59
|
end
|
268
60
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
61
|
+
context 'tokenized credit card flow master' do
|
62
|
+
before(:each) do
|
63
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
64
|
+
@properties = build_pm_properties(nil,
|
65
|
+
{
|
66
|
+
:cc_number => 5555555555554444,
|
67
|
+
:cc_type => 'master',
|
68
|
+
:payment_cryptogram => cryptogram
|
69
|
+
})
|
70
|
+
@pm = create_payment_method(::Killbill::Orbital::OrbitalPaymentMethod, nil, @call_context.tenant_id, @properties, {})
|
71
|
+
@amount = BigDecimal.new('100')
|
72
|
+
@currency = 'USD'
|
73
|
+
end
|
274
74
|
|
275
|
-
|
276
|
-
<<-XML
|
277
|
-
<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><NewOrderResp><IndustryType></IndustryType><MessageType>AC</MessageType><MerchantID>1111111</MerchantID><TerminalID>001</TerminalID><CardBrand>CC</CardBrand><AccountNum>XXXXXXXXXXXX5454</AccountNum><OrderID>88132d30-f4f7-4028-949</OrderID><TxRefNum>5834EAC9C7A53FEB600A479629FB6C6427A2532C</TxRefNum><TxRefIdx>1</TxRefIdx><ProcStatus>0</ProcStatus><ApprovalStatus>1</ApprovalStatus><RespCode>00</RespCode><AVSRespCode>3 </AVSRespCode><CVV2RespCode>I</CVV2RespCode><AuthCode>tst703</AuthCode><RecurringAdviceCd></RecurringAdviceCd><CAVVRespCode></CAVVRespCode><StatusMsg>Approved</StatusMsg><RespMsg></RespMsg><HostRespCode>100</HostRespCode><HostAVSRespCode> </HostAVSRespCode><HostCVV2RespCode>I</HostCVV2RespCode><CustomerRefNum></CustomerRefNum><CustomerName></CustomerName><ProfileProcStatus></ProfileProcStatus><CustomerProfileMessage></CustomerProfileMessage><RespTime>200306</RespTime><PartialAuthOccurred></PartialAuthOccurred><RequestedAmount></RequestedAmount><RedeemedAmount></RedeemedAmount><RemainingBalance></RemainingBalance><CountryFraudFilterStatus></CountryFraudFilterStatus><IsoCountryCode></IsoCountryCode></NewOrderResp></Response> XML
|
278
|
-
XML
|
75
|
+
include_examples 'payment_flow_spec'
|
279
76
|
end
|
280
77
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
78
|
+
context 'tokenized credit card flow discover' do
|
79
|
+
before(:each) do
|
80
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
81
|
+
@properties = build_pm_properties(nil,
|
82
|
+
{
|
83
|
+
:cc_number => 6011111111111117,
|
84
|
+
:cc_type => 'discover',
|
85
|
+
:payment_cryptogram => cryptogram
|
86
|
+
})
|
87
|
+
@pm = create_payment_method(::Killbill::Orbital::OrbitalPaymentMethod, nil, @call_context.tenant_id, @properties, {})
|
88
|
+
@amount = BigDecimal.new('100')
|
89
|
+
@currency = 'USD'
|
293
90
|
end
|
294
|
-
create_payment
|
295
|
-
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
296
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
297
|
-
payment_response.amount.should == @amount
|
298
|
-
payment_response.transaction_type.should == :AUTHORIZE
|
299
91
|
|
300
|
-
|
301
|
-
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
302
|
-
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
303
|
-
payment_response.amount.should == @amount
|
304
|
-
payment_response.transaction_type.should == :PURCHASE
|
92
|
+
include_examples 'payment_flow_spec'
|
305
93
|
end
|
306
94
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
@
|
95
|
+
context 'tokenized credit card flow visa' do
|
96
|
+
before(:each) do
|
97
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
98
|
+
@properties = build_pm_properties(nil,
|
99
|
+
{
|
100
|
+
:cc_number => 4112344112344113,
|
101
|
+
:cc_type => 'visa',
|
102
|
+
:payment_cryptogram => cryptogram
|
103
|
+
})
|
104
|
+
@pm = create_payment_method(::Killbill::Orbital::OrbitalPaymentMethod, nil, @call_context.tenant_id, @properties, {})
|
105
|
+
@amount = BigDecimal.new('100')
|
106
|
+
@currency = 'USD'
|
311
107
|
end
|
312
|
-
|
108
|
+
|
109
|
+
include_examples 'payment_flow_spec'
|
313
110
|
end
|
314
111
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
shared_examples 'payment_flow_spec' do
|
2
|
+
|
3
|
+
before(:each) do
|
4
|
+
create_payment
|
5
|
+
end
|
6
|
+
|
7
|
+
after(:each) do
|
8
|
+
@plugin.stop_plugin
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be able to purchase' do
|
12
|
+
Killbill::Orbital::OrbitalResponse.all.size.should == 1
|
13
|
+
Killbill::Orbital::OrbitalTransaction.all.size.should == 0
|
14
|
+
|
15
|
+
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
16
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
17
|
+
payment_response.amount.should == @amount
|
18
|
+
payment_response.transaction_type.should == :PURCHASE
|
19
|
+
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
20
|
+
|
21
|
+
responses = Killbill::Orbital::OrbitalResponse.all
|
22
|
+
responses.size.should == 2
|
23
|
+
responses[0].api_call.should == 'add_payment_method'
|
24
|
+
responses[0].message.should == 'Profile Request Processed'
|
25
|
+
responses[1].api_call.should == 'purchase'
|
26
|
+
responses[1].message.should == 'Approved'
|
27
|
+
transactions = Killbill::Orbital::OrbitalTransaction.all
|
28
|
+
transactions.size.should == 1
|
29
|
+
transactions[0].api_call.should == 'purchase'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should be able to charge and refund' do
|
33
|
+
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
34
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
35
|
+
payment_response.amount.should == @amount
|
36
|
+
payment_response.transaction_type.should == :PURCHASE
|
37
|
+
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
38
|
+
|
39
|
+
# Try a full refund
|
40
|
+
refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
41
|
+
refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
|
42
|
+
refund_response.amount.should == @amount
|
43
|
+
refund_response.transaction_type.should == :REFUND
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should be able to auth, capture and refund' do
|
47
|
+
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
48
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
49
|
+
payment_response.amount.should == @amount
|
50
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
51
|
+
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
52
|
+
|
53
|
+
# Try multiple partial captures
|
54
|
+
partial_capture_amount = BigDecimal.new('10')
|
55
|
+
1.upto(3) do |i|
|
56
|
+
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[i].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
57
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
58
|
+
payment_response.amount.should == partial_capture_amount
|
59
|
+
payment_response.transaction_type.should == :CAPTURE
|
60
|
+
end
|
61
|
+
|
62
|
+
# Try a partial refund
|
63
|
+
refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[4].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
64
|
+
refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
|
65
|
+
refund_response.amount.should == partial_capture_amount
|
66
|
+
refund_response.transaction_type.should == :REFUND
|
67
|
+
|
68
|
+
# Try to capture again
|
69
|
+
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[5].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
70
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
71
|
+
payment_response.amount.should == partial_capture_amount
|
72
|
+
payment_response.transaction_type.should == :CAPTURE
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should be able to auth and void' do
|
76
|
+
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
77
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
78
|
+
payment_response.amount.should == @amount
|
79
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
80
|
+
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
81
|
+
|
82
|
+
payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @properties, @call_context)
|
83
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
84
|
+
payment_response.transaction_type.should == :VOID
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should be able to auth, partial capture and void' do
|
88
|
+
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
89
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
90
|
+
payment_response.amount.should == @amount
|
91
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
92
|
+
find_value_from_properties(payment_response.properties, 'processorResponse').should == '100'
|
93
|
+
|
94
|
+
partial_capture_amount = BigDecimal.new('10')
|
95
|
+
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
96
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
97
|
+
payment_response.amount.should == partial_capture_amount
|
98
|
+
payment_response.transaction_type.should == :CAPTURE
|
99
|
+
|
100
|
+
payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[2].id, @pm.kb_payment_method_id, @properties, @call_context)
|
101
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
102
|
+
payment_response.transaction_type.should == :VOID
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should be able to credit' do
|
106
|
+
payment_response = @plugin.credit_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
107
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
108
|
+
payment_response.amount.should == @amount
|
109
|
+
payment_response.transaction_type.should == :CREDIT
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should include host response code' do
|
113
|
+
# Sending a specific amount of 530 will trigger the Do Not Honor error.
|
114
|
+
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, BigDecimal.new('530'), @currency, @properties, @call_context)
|
115
|
+
payment_response.status.should eq(:ERROR), payment_response.gateway_error
|
116
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
117
|
+
payment_response.amount.should be_nil
|
118
|
+
find_value_from_properties(payment_response.properties, 'processorResponse').should == '530'
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
ActiveMerchant::Billing::Base.mode = :test
|
4
|
+
|
5
|
+
describe 'Payment request for network tokenized card' do
|
6
|
+
|
7
|
+
include ::Killbill::Plugin::ActiveMerchant::RSpec
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
# Start the plugin early to configure ActiveRecord
|
11
|
+
@plugin = build_plugin(::Killbill::Orbital::PaymentPlugin, 'orbital')
|
12
|
+
@plugin.start_plugin
|
13
|
+
|
14
|
+
::Killbill::Orbital::OrbitalPaymentMethod.delete_all
|
15
|
+
::Killbill::Orbital::OrbitalResponse.delete_all
|
16
|
+
::Killbill::Orbital::OrbitalTransaction.delete_all
|
17
|
+
|
18
|
+
@call_context = build_call_context
|
19
|
+
end
|
20
|
+
|
21
|
+
after(:each) do
|
22
|
+
@plugin.stop_plugin
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should send correct payload for visa network tokenized card' do
|
26
|
+
|
27
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
28
|
+
@properties = build_pm_properties(nil,
|
29
|
+
{
|
30
|
+
:cc_number => 4111111111111111,
|
31
|
+
:cc_type => 'visa',
|
32
|
+
:payment_cryptogram => cryptogram
|
33
|
+
})
|
34
|
+
@amount = BigDecimal.new('100')
|
35
|
+
@currency = 'USD'
|
36
|
+
|
37
|
+
::ActiveMerchant::Billing::OrbitalGateway.any_instance.stub(:ssl_post) do |host, request_body|
|
38
|
+
request_body.should match("<CAVV>#{cryptogram}</CAVV>")
|
39
|
+
request_body.should match('<DPANInd>Y</DPANInd>')
|
40
|
+
request_body.should match("<DigitalTokenCryptogram>#{cryptogram}</DigitalTokenCryptogram>")
|
41
|
+
|
42
|
+
successful_authorize_response
|
43
|
+
end
|
44
|
+
|
45
|
+
validate_payment
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should send correct payload for amex network tokenized card' do
|
49
|
+
|
50
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
51
|
+
@properties = build_pm_properties(nil,
|
52
|
+
{
|
53
|
+
:cc_number => 378282246310005,
|
54
|
+
:cc_type => 'american_express',
|
55
|
+
:payment_cryptogram => cryptogram,
|
56
|
+
:eci => '7',
|
57
|
+
})
|
58
|
+
@amount = BigDecimal.new('100')
|
59
|
+
@currency = 'USD'
|
60
|
+
|
61
|
+
::ActiveMerchant::Billing::OrbitalGateway.any_instance.stub(:ssl_post) do |host, request_body|
|
62
|
+
request_body.should match("<AEVV>#{cryptogram}</AEVV>")
|
63
|
+
request_body.should match('<DPANInd>Y</DPANInd>')
|
64
|
+
request_body.should match("<DigitalTokenCryptogram>#{cryptogram}</DigitalTokenCryptogram>")
|
65
|
+
request_body.should match('<AuthenticationECIInd>7</AuthenticationECIInd>')
|
66
|
+
|
67
|
+
successful_authorize_response
|
68
|
+
end
|
69
|
+
|
70
|
+
validate_payment
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should send correct payload for master network tokenized card' do
|
74
|
+
|
75
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
76
|
+
@properties = build_pm_properties(nil,
|
77
|
+
{
|
78
|
+
:cc_number => 5555555555554444,
|
79
|
+
:cc_type => 'master',
|
80
|
+
:payment_cryptogram => cryptogram,
|
81
|
+
})
|
82
|
+
@amount = BigDecimal.new('100')
|
83
|
+
@currency = 'USD'
|
84
|
+
|
85
|
+
::ActiveMerchant::Billing::OrbitalGateway.any_instance.stub(:ssl_post) do |host, request_body|
|
86
|
+
request_body.should match("<AAV>#{cryptogram}</AAV>")
|
87
|
+
request_body.should match('<DPANInd>Y</DPANInd>')
|
88
|
+
request_body.should match("<DigitalTokenCryptogram>#{cryptogram}</DigitalTokenCryptogram>")
|
89
|
+
|
90
|
+
successful_authorize_response
|
91
|
+
end
|
92
|
+
|
93
|
+
validate_payment
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should send correct payload for discover network tokenized card' do
|
97
|
+
|
98
|
+
cryptogram = 'EHuWW9PiBkWvqE5juRwDzAUFBAk='
|
99
|
+
@properties = build_pm_properties(nil,
|
100
|
+
{
|
101
|
+
:cc_number => 6011111111111117,
|
102
|
+
:cc_type => 'discover',
|
103
|
+
:payment_cryptogram => cryptogram
|
104
|
+
})
|
105
|
+
@amount = BigDecimal.new('100')
|
106
|
+
@currency = 'USD'
|
107
|
+
|
108
|
+
::ActiveMerchant::Billing::OrbitalGateway.any_instance.stub(:ssl_post) do |host, request_body|
|
109
|
+
request_body.should match('<DPANInd>Y</DPANInd>')
|
110
|
+
request_body.should match("<DigitalTokenCryptogram>#{cryptogram}</DigitalTokenCryptogram>")
|
111
|
+
|
112
|
+
successful_authorize_response
|
113
|
+
end
|
114
|
+
|
115
|
+
validate_payment
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def successful_authorize_response
|
121
|
+
<<-XML
|
122
|
+
<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><NewOrderResp><IndustryType></IndustryType><MessageType>A</MessageType><MerchantID>1111111</MerchantID><TerminalID>001</TerminalID><CardBrand>CC</CardBrand><AccountNum>XXXXXXXXXXXX5454</AccountNum><OrderID>5b257b31-1f84-44bc-b32</OrderID><TxRefNum>5834AA75E4466AEA59512165057C37DD810053C2</TxRefNum><TxRefIdx>0</TxRefIdx><ProcStatus>0</ProcStatus><ApprovalStatus>1</ApprovalStatus><RespCode>00</RespCode><AVSRespCode>B </AVSRespCode><CVV2RespCode> </CVV2RespCode><AuthCode>tst424</AuthCode><RecurringAdviceCd></RecurringAdviceCd><CAVVRespCode></CAVVRespCode><StatusMsg>Approved</StatusMsg><RespMsg></RespMsg><HostRespCode>100</HostRespCode><HostAVSRespCode>I3</HostAVSRespCode><HostCVV2RespCode> </HostCVV2RespCode><CustomerRefNum></CustomerRefNum><CustomerName></CustomerName><ProfileProcStatus></ProfileProcStatus><CustomerProfileMessage></CustomerProfileMessage><RespTime>152837</RespTime><PartialAuthOccurred></PartialAuthOccurred><RequestedAmount></RequestedAmount><RedeemedAmount></RedeemedAmount><RemainingBalance></RemainingBalance><CountryFraudFilterStatus></CountryFraudFilterStatus><IsoCountryCode></IsoCountryCode></NewOrderResp></Response>
|
123
|
+
XML
|
124
|
+
end
|
125
|
+
|
126
|
+
def validate_payment
|
127
|
+
kb_payment_id, kb_transaction_id = create_payment
|
128
|
+
payment_response = @plugin.authorize_payment(SecureRandom.uuid, kb_payment_id, kb_transaction_id, SecureRandom.uuid, @amount, @currency, @properties, @call_context)
|
129
|
+
payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
|
130
|
+
payment_response.amount.should == @amount
|
131
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -22,3 +22,11 @@ ActiveRecord::Base.establish_connection(
|
|
22
22
|
# Create the schema
|
23
23
|
require File.expand_path(File.dirname(__FILE__) + '../../db/schema.rb')
|
24
24
|
|
25
|
+
def create_payment
|
26
|
+
kb_payment_id = SecureRandom.uuid
|
27
|
+
1.upto(6) do
|
28
|
+
@kb_payment = @plugin.kb_apis.proxied_services[:payment_api].add_payment(kb_payment_id)
|
29
|
+
end
|
30
|
+
[kb_payment_id, @kb_payment.transactions[0].id]
|
31
|
+
end
|
32
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: killbill-orbital
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kill Bill core team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -317,7 +317,10 @@ files:
|
|
317
317
|
- pom.xml
|
318
318
|
- release.sh
|
319
319
|
- spec/orbital/base_plugin_spec.rb
|
320
|
+
- spec/orbital/cvv_indicator_spec.rb
|
320
321
|
- spec/orbital/remote/integration_spec.rb
|
322
|
+
- spec/orbital/remote/shared_examples_for_payment_flow.rb
|
323
|
+
- spec/orbital/tokenized_creditcard_payload_spec.rb
|
321
324
|
- spec/spec_helper.rb
|
322
325
|
homepage: http://killbill.io
|
323
326
|
licenses:
|