offsite_payments_migs 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 70fe4e7f16a7259ddbd7d02438db6489d87f8dc4
4
+ data.tar.gz: 600547c5546a2c67c907d01aff2c0d5a07716f7b
5
+ SHA512:
6
+ metadata.gz: 43d778c0d1a020cb0294d1dafd4b69480d55ea9928051fc69cb165b78ef689a0e7baaf6ef074fd0683b00e6cca8d070f495a1c19ae5b76f8c100f8ba98b2cff6
7
+ data.tar.gz: 820c77803bbceca7e096fc97c9d8e585b41cc00e4a41d7c5311e2f8bf1da64df3ceb265f16596bca4d5ea8f3ab9080a5c3253692294ce8d055d2f3285cdaef52
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2014 Tobias Luetke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Offsite Payments
2
+
3
+ Offsite Payments is an extraction from the ecommerce system [Shopify](http://www.shopify.com). Shopify's requirements for a simple and unified API to handle dozens of different offsite payment pages (often called hosted payment pages) with very different exposed APIs was the chief principle in designing the library.
4
+
5
+ It was developed for usage in Ruby on Rails web applications and integrates seamlessly
6
+ as a Rails plugin. It should also work as a stand alone Ruby library, but much of the benefit is in the ActionView helpers which are Rails-specific.
7
+
8
+ This gem provides integration for [MiGS](https://en.wikipedia.org/wiki/Mastercard_Internet_Gateway_Service)
9
+ through the Offsite Payments gem.
10
+
11
+ ## Installation
12
+
13
+ ### From Git
14
+
15
+ You can check out the latest source from git:
16
+
17
+ git clone https://github.com/sealink/offsite_payments_migs.git
18
+
19
+ ### From RubyGems
20
+
21
+ Installation from RubyGems:
22
+
23
+ gem install offsite_payments_migs
24
+
25
+ Or, if you're using Bundler, just add the following to your Gemfile:
26
+
27
+ gem 'offsite_payments_migs'
28
+
29
+ ## Misc.
30
+
31
+ - This library is MIT licensed.
@@ -0,0 +1,236 @@
1
+ module OffsitePayments
2
+ module Integrations
3
+ module Migs
4
+ API_VERSION = 1
5
+
6
+ def self.return(query_string, options = {})
7
+ Return.new(query_string, options)
8
+ end
9
+
10
+ class Helper < OffsitePayments::Helper
11
+ def self.base_url
12
+ 'https://migs.mastercard.com.au/vpcpay'
13
+ end
14
+
15
+ def initialize(order, account, options = {})
16
+ @credentials = { login: account, password: options.fetch(:password) }
17
+ @secure_hash = options.fetch(:secure_hash)
18
+ @options = options.merge(order_id: order)
19
+ end
20
+
21
+ # Generates a URL to redirect user to MiGS to process payment
22
+ # Once user is finished MiGS will redirect back to specified URL
23
+ # With a response hash which can be turned into a Response object
24
+ # with purchase_offsite_response
25
+ #
26
+ # ==== Options
27
+ #
28
+ # * <tt>:order_id</tt> -- A reference for tracking the order (REQUIRED)
29
+ # * <tt>:locale</tt> -- Change the language of the redirected page
30
+ # Values are 2 digit locale, e.g. en, es
31
+ # * <tt>:return_url</tt> -- the URL to return to once the payment is complete
32
+ # * <tt>:card_type</tt> -- Providing this skips the card type step.
33
+ # Values are ActiveMerchant formats: e.g. master, visa, american_express, diners_club
34
+ # * <tt>:unique_id</tt> -- Unique id of transaction to find.
35
+ # If not supplied one will be generated.
36
+ def credential_based_url
37
+ cents = @options.fetch(:cents)
38
+ builder = TransactionBuilder.new(@credentials)
39
+ builder.add_invoice(@options)
40
+ builder.add_creditcard_type(@options[:card_type]) if @options[:card_type]
41
+ builder.add_amount(cents)
42
+ builder.add_standard_parameters('pay', @options[:unique_id])
43
+ post = builder.post.merge(
44
+ Locale: @options[:locale] || 'en',
45
+ ReturnURL: @options.fetch(:return_url)
46
+ )
47
+ post[:SecureHash] = SecureHash.calculate(@secure_hash, post)
48
+ post[:SecureHashType] = 'SHA256'
49
+
50
+ self.class.base_url + '?' + post_data(post)
51
+ end
52
+
53
+ private
54
+
55
+ def post_data(post)
56
+ post.collect { |key, value| "vpc_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
57
+ end
58
+ end
59
+
60
+ class Notification < OffsitePayments::Notification
61
+ def initialize(params, options = {})
62
+ @params = params
63
+ @response = parse
64
+ @options = options
65
+ end
66
+
67
+ def parse
68
+ @params.map.with_object({}) { |(key, value), hash|
69
+ hash[key.gsub('vpc_', '').to_sym] = value
70
+ }
71
+ end
72
+
73
+ def avs_response_code
74
+ avs_response_code = @response[:AVSResultCode]
75
+ avs_response_code = 'S' if avs_response_code == "Unsupported"
76
+ avs_response_code
77
+ end
78
+
79
+ def cvv_result_code
80
+ cvv_result_code = @response[:CSCResultCode]
81
+ cvv_result_code = 'P' if cvv_result_code == "Unsupported"
82
+ cvv_result_code
83
+ end
84
+
85
+ def success?
86
+ @response[:TxnResponseCode] == '0'
87
+ end
88
+
89
+ def fraud_review?
90
+ ISSUER_RESPONSE_CODES[@response[:AcqResponseCode]] == 'Suspected Fraud'
91
+ end
92
+
93
+ def acknowledge
94
+ # Failures don't include a secure hash, so return directly
95
+ return true unless success?
96
+
97
+ # Check SecureHash only on success (not returned otherwise)
98
+ unless @response[:SecureHash] == expected_secure_hash
99
+ raise SecurityError, "Secure Hash mismatch, response may be tampered with"
100
+ end
101
+
102
+ true
103
+ end
104
+
105
+ def expected_secure_hash
106
+ SecureHash.calculate(@options[:secure_hash], @response)
107
+ end
108
+
109
+ def gross
110
+ @response[:Amount].to_d / 100.0
111
+ end
112
+
113
+ def card_code
114
+ return unless @response.key?(:Card) # Card doesn't appear on failure
115
+ migs_code = @response[:Card]
116
+ CARD_TYPES.detect { |ct|
117
+ ct.migs_code == migs_code
118
+ }.am_code
119
+ end
120
+
121
+ def order_id
122
+ @response[:OrderInfo]
123
+ end
124
+
125
+ def uid
126
+ @response[:MerchTxnRef]
127
+ end
128
+
129
+ def transaction_id
130
+ @response[:TransactionNo]
131
+ end
132
+
133
+ def message # only when error
134
+ @response['Message']
135
+ end
136
+ end
137
+
138
+ def test?
139
+ # TEST prefix defines if login is for test system, see page 37 of:
140
+ # https://anz.com.au/australia/business/merchant/pdf/MIGSProductGuide.pdf
141
+ @options[:login].start_with?('TEST')
142
+ end
143
+
144
+ class SecureHash
145
+ require 'openssl'
146
+
147
+ DIGEST = OpenSSL::Digest.new('sha256')
148
+
149
+ def self.calculate(secure_hash, post)
150
+ post_without_secure_hash = post.reject { |k, _v| [:SecureHash, :SecureHashType].include? k }
151
+ sorted_values = post_without_secure_hash.sort_by(&:to_s).map { |key, value| "vpc_#{key}=#{value}"}
152
+ input = sorted_values.join('&')
153
+ OpenSSL::HMAC.hexdigest(DIGEST, [secure_hash].pack('H*'), input).upcase
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ class CreditCardType
160
+ attr_accessor :am_code, :migs_code, :migs_long_code, :name
161
+ def initialize(am_code, migs_code, migs_long_code, name)
162
+ @am_code = am_code
163
+ @migs_code = migs_code
164
+ @migs_long_code = migs_long_code
165
+ @name = name
166
+ end
167
+ end
168
+
169
+ CARD_TYPES = [
170
+ # The following are 4 different representations of credit card types
171
+ # am_code: The active merchant code
172
+ # migs_code: Used in response for purchase/authorize/status
173
+ # migs_long_code: Used to pre-select card for server_purchase_url
174
+ # name: The nice display name
175
+ %w(american_express AE Amex American\ Express),
176
+ %w(diners_club DC Dinersclub Diners\ Club),
177
+ %w(jcb JC JCB JCB\ Card),
178
+ %w(maestro MS Maestro Maestro\ Card),
179
+ %w(master MC Mastercard MasterCard),
180
+ %w(na PL PrivateLabelCard Private\ Label\ Card),
181
+ %w(visa VC Visa Visa\ Card')
182
+ ].map { |am_code, migs_code, migs_long_code, name|
183
+ CreditCardType.new(am_code, migs_code, migs_long_code, name)
184
+ }
185
+
186
+ class TransactionBuilder
187
+ attr_reader :post
188
+
189
+ def initialize(options)
190
+ @options = options
191
+ @post = {}
192
+ end
193
+
194
+ def add_invoice(options)
195
+ post[:OrderInfo] = options.fetch(:order_id)
196
+ end
197
+
198
+ def add_amount(cents)
199
+ post[:Amount] = cents.to_s
200
+ end
201
+
202
+ def add_creditcard(creditcard)
203
+ post[:CardNum] = creditcard.number
204
+ post[:CardSecurityCode] = creditcard.verification_value if creditcard.verification_value?
205
+ post[:CardExp] = format(creditcard.year) + format(creditcard.month)
206
+ end
207
+
208
+ def add_creditcard_type(card_type)
209
+ post[:Gateway] = 'ssl'
210
+ post[:card] = CARD_TYPES.detect{|ct| ct.am_code == card_type}.migs_long_code
211
+ end
212
+
213
+ def add_advanced_user
214
+ post[:User] = @options[:advanced_login]
215
+ post[:Password] = @options[:advanced_password]
216
+ end
217
+
218
+ def add_standard_parameters(action, unique_id = nil)
219
+ post.merge!(
220
+ :Version => API_VERSION,
221
+ :Merchant => @options[:login],
222
+ :AccessCode => @options[:password],
223
+ :Command => action,
224
+ :MerchTxnRef => unique_id || SecureRandom.hex(16).slice(0, 40)
225
+ )
226
+ end
227
+
228
+ private
229
+
230
+ def format(number)
231
+ sprintf("%.2i", number.to_i)[-2..-1]
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,3 @@
1
+ module OffsitePaymentsMigs
2
+ VERSION = "1.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ module OffsitePaymentsMigs
2
+ require 'offsite_payments'
3
+ require_relative 'offsite_payments/integrations/migs'
4
+
5
+ mattr_accessor :mode
6
+ self.mode = :production
7
+
8
+ # A check to see if we're in test mode
9
+ def self.test?
10
+ self.mode == :test
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: offsite_payments_migs
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stefan Cooper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: offsite_payments
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: This gem extends the activemerchant offsite_payments gem providing integration
56
+ of MiGS.
57
+ email: stefan.cooper@sealink.com.au
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - MIT-LICENSE
63
+ - README.md
64
+ - lib/offsite_payments/integrations/migs.rb
65
+ - lib/offsite_payments_migs.rb
66
+ - lib/offsite_payments_migs/version.rb
67
+ homepage: https://github.com/sealink/offsite_payments_migs
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.4.5.1
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: MiGS integration for the activemerchant offsite_payments gem.
91
+ test_files: []