offsite_payments_migs 1.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +31 -0
- data/lib/offsite_payments/integrations/migs.rb +236 -0
- data/lib/offsite_payments_migs/version.rb +3 -0
- data/lib/offsite_payments_migs.rb +12 -0
- metadata +91 -0
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,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: []
|