vantiv_sftp_reports 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bfe911dff287b7ee8893c86f18dfc091e11bbe03
4
+ data.tar.gz: c68689b05a2b7d9f084d08460e4cd51eeecbfd4d
5
+ SHA512:
6
+ metadata.gz: b864cf767f98c214bf50bba248199e7fa43fad8bfc74344adf7d714efc1b8de8d0b373f4f91d400986ecc9bec9a5ef70ef84ebe28cb0d7ec2eaaf1e29e71e7e4
7
+ data.tar.gz: 348b2b5bd79c83d8c126cbb814db3cc455c4725b0baec0ab5d39f2e0c70f298121ca255ebd5b6e052ce841f6f44356a23cf4257e26062405f815cf2618bc08ac
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Joshua Hansen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # Vantiv SFTP Reports
2
+
3
+ Are you using WorldPay/Vantiv/LitleOnline and need to actually resolve what happened with a particular transaction or just need to be able to consume data about what's going on in a programmatic way? Funny story about that...
4
+
5
+ The eCommerce "solution" (Vantiv's CNP API aka LitleOnline) provides no APIs for handling this because who on earth would actually want to get to their data via an API and have historical access, right? It gets better though because their hosted payments solution---a solution designed for people trying to avoid API integrations---actually has an API for this sort of thing. Does this make any sense? No. No, it does not.
6
+
7
+ With that said, let's party like it's 1999! (At least SFTP was made in this century I guess.) You can make like a digital Neanderthal and download report files over SFTP, parse CSVs, and get whatever you need.
8
+
9
+ (If you ever wondered why [Stripe](https://stripe.com) is so successful, this sort of absurdity has something to do with it. They actually get that developers are a thing.)
10
+
11
+ This library is meant to be pretty low level. It just handles some basic configuration, downloading, and parsing. It is not meant to care about the particulars of how those reports are consumed, related, or even which reports you have enabled.
12
+
13
+ ## Installation
14
+
15
+ The usual:
16
+
17
+ $ gem install vantiv_sftp_reports
18
+
19
+ ## Configuration
20
+
21
+ As there is no sandbox for testing this, you're going to need to do a bit of configuring to get anything out of this. This gem supports managing multiple configurations, but the vast majority of use cases revolve around a single default configuration.
22
+
23
+ I can be set one of two ways:
24
+
25
+ ## Use a Hash
26
+
27
+ ```ruby
28
+ VantivSFTPReports.configure(
29
+ host:, # SFTP host, defaults to 'reports.iq.vantivcnp.com'
30
+ organization_id:, # Organization ID for reports, leave blank if you only have one organization
31
+ password:, # Your SFTP username
32
+ path:, # Directory where reports are stored, defaults to 'reports'
33
+ username: # Your SFTP username
34
+ )
35
+ ```
36
+
37
+ This sets the default configuration (`VantivSFTPReports.default_config`).
38
+
39
+ ## Infer Values from `ENV`
40
+
41
+ Prefix any configuration option with `vantiv_sftp_` and it will be automatically set:
42
+
43
+ * `ENV['vantiv_sftp_host']`
44
+ * `ENV['vantiv_sftp_organization_id']`
45
+ * `ENV['vantiv_sftp_password']`
46
+ * `ENV['vantiv_sftp_path']`
47
+ * `ENV['vantiv_sftp_username']`
48
+
49
+ ## Usage
50
+
51
+ The `VantivSFTPReports::Fetch` class is used for actually getting reports. In a nutshell it takes information about the reports you're looking for, downloads the files, and parses the CSVs provided into [`CSV::Table`](https://ruby-doc.org/stdlib-2.2.7/libdoc/csv/rdoc/CSV/Table.html) objects. Each instance of `Fetch` can be loaded with a custom configuration, but if none is given will use the default global configuration.
52
+
53
+ So, if you're supporting multiple logins you can use:
54
+
55
+ ```ruby
56
+ fetch1 = VantivSFTPReports::Fetch.new(username: 'user1')
57
+ fetch2 = VantivSFTPReports::Fetch.new(username: 'user2')
58
+ ```
59
+
60
+ A fetch object will pull down reports by name, date, and organization ID. So, if you wanted to get a report named `Transactional_Detail_SessionByActivityDate` for the last 3 days for organization `1234` you could do the following:
61
+
62
+ ```ruby
63
+ reports = VantivSFTPReports::Fetch.new.call(
64
+ 'Transactional_Detail_SessionByActivityDate',
65
+ by_date: (Date.today - 2)..Date.today,
66
+ by_organization_id: '1234'
67
+ )
68
+ ```
69
+
70
+ If you're using a single configuration and don't need to anything particularly sophisticated, you can use the following abreviated method:
71
+
72
+ ```ruby
73
+ reports = VantivSFTPReports.fetch(*args)
74
+ ```
75
+
76
+ ## A Specific Example
77
+
78
+ Let's say I wanted to get the batch ID associated with a transaction ID from yesterday:
79
+
80
+ ```ruby
81
+ report = VantivSFTPReports.first(
82
+ 'Transactional_Detail_SessionByActivityDate',
83
+ by_date: (Date.today - 1)
84
+ ) # returns only one report regardless of results
85
+
86
+ report.each_with_object({}) { |r, h| h[r[:vantiv_payment_id]] = r[:batch_id] }
87
+ ```
88
+
89
+ ## Testing with Sandbox or Prelive
90
+
91
+ So far as I know, there is no way to test the reporting features with the sandbox. There are no sample reports. Furthermore, while this **might** be possible using Prelive, the customer service team I've dealt with is still "looking into that™".
92
+
93
+ ## What about the actual APIs that do exist?
94
+
95
+ I have a [low dependency gem for that](https://github.com/binarypaladin/vanitv_lite) that doesn't force you to use ActiveSupport and is XML library agnostic.
96
+
97
+ Or, you can always use [the official one](https://github.com/Vantiv/litle-sdk-for-ruby).
98
+
99
+ ## Contributing
100
+
101
+ ### Issue Guidelines
102
+
103
+ GitHub issues are for bugs, not support. As of right now, there is no official support for this gem. You can try reaching out to the author, [Joshua Hansen](mailto:joshua@epicbanality.com?subject=VantivSFTPReports) if you're really stuck, but there's a pretty high chance that won't go anywhere at the moment or you'll get a response like this:
104
+
105
+ > Hi. I'm super busy. It's nothing personal. Check the README first if you haven't already. If you don 't find your answer there, it's time to start reading the source. Have fun! Let me know if I screwed something up.
106
+
107
+ ### Pull Request Guidelines
108
+
109
+ * Include tests with your PRs.
110
+ * Run `rubocop` to ensure your style fits with the rest of the project.
111
+
112
+ ## License
113
+
114
+ See [`LICENSE.txt`](LICENSE.txt).
115
+
116
+ ## What if I stop maintaining this?
117
+
118
+ The codebase isn't huge. If you opt to rely on this code and I die/get bored/find enlightenment you should be able to maintain it. Sadly, that's the only guarantee at the moment!
@@ -0,0 +1,36 @@
1
+ # frozen-string-literal: true
2
+
3
+ module VantivSFTPReports
4
+ class Config
5
+ def self.with_obj(config)
6
+ config.is_a?(self) ? config : new(config)
7
+ end
8
+
9
+ def initialize(**opts)
10
+ @opts = opts.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
11
+ @sftp_opts = opts.reject { |k, _| %i[host organization_id path username].include?(k) }
12
+ defaults!
13
+ end
14
+
15
+ def opts
16
+ @opts.dup
17
+ end
18
+
19
+ def sftp_opts
20
+ @sftp_opts.dup
21
+ end
22
+
23
+ def with(**opts)
24
+ self.class.new(@opts.merge(opts))
25
+ end
26
+
27
+ %i[host organization_id password path username].each { |o| define_method(o) { @opts[o] } }
28
+
29
+ private
30
+
31
+ def defaults!
32
+ @opts[:host] ||= 'reports.iq.vantivcnp.com'
33
+ @opts[:path] ||= 'reports'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,80 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'csv'
4
+ require 'vantiv_sftp_reports/config'
5
+
6
+ module VantivSFTPReports
7
+ class Fetch
8
+ class << self
9
+ %i[call download list].each do |m|
10
+ define_method(m) { |*args| new.__send__(m, *args) }
11
+ end
12
+ end
13
+
14
+ attr_reader :config
15
+
16
+ def initialize(config = VantivSFTPReports.default_config)
17
+ @config = Config.with_obj(config)
18
+ end
19
+
20
+ def call(*args)
21
+ download(*list(*args)).each_with_object({}) do |(filename, csv), h|
22
+ next unless csv
23
+ h[filename] = parse_report(csv)
24
+ end
25
+ end
26
+
27
+ def download(*filenames)
28
+ {}.tap do |h|
29
+ session do |sftp|
30
+ filenames.each { |fname| h[fname] = sftp.download!("#{path}/#{fname}") }
31
+ end
32
+ end
33
+ end
34
+
35
+ def first(*args)
36
+ (report = call(*args).first) && report.last
37
+ end
38
+
39
+ def list(pattern = nil, by_date: Date.today, by_organization_id: organization_id)
40
+ [].tap do |list|
41
+ session { |sftp| sftp.dir.foreach(path) { |e| list << e.name unless e.name[0] == '.' } }
42
+ filter_by_date!(list, by_date) if by_date
43
+ filter_by_organization_id!(list, by_organization_id) if organization_id
44
+ filter_by_pattern!(list, pattern) if pattern
45
+ end
46
+ end
47
+
48
+ def session
49
+ Net::SFTP.start(config.host, config.username, config.sftp_opts) { |sftp| yield(sftp) }
50
+ end
51
+
52
+ %i[organization_id path].each { |m| define_method(m) { config.__send__(m) } }
53
+
54
+ private
55
+
56
+ def date_str(obj)
57
+ obj.respond_to?(:strftime) ? obj.strftime('%Y%m%d') : obj.to_s
58
+ end
59
+
60
+ def filter_by_date!(list, dates)
61
+ list.select! { |r| r =~ /_(#{Array(dates).map { |d| date_str(d) }.join('|')})\.CSV\Z/ }
62
+ end
63
+
64
+ def filter_by_organization_id!(list, organization_id)
65
+ list.select! { |r| r =~ /_#{organization_id}_/ }
66
+ end
67
+
68
+ def filter_by_pattern!(list, pattern)
69
+ pattern = pattern.is_a?(Regexp) ? pattern : /^#{pattern}/i
70
+ list.select! { |r| r =~ pattern }
71
+ end
72
+
73
+ # To avoid numberic conversion, IDs from Vantiv are prefixed with a single
74
+ # quote. Since no conversions are done here, this is nothing but an
75
+ # inconvenience and is stripped.
76
+ def parse_report(str)
77
+ CSV.parse(str.gsub(/'(\d{18})/, '\1'), headers: true, header_converters: :symbol)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,12 @@
1
+ # frozen-string-literal: true
2
+
3
+ module VantivSFTPReports
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 0
7
+ VERSION = [MAJOR, MINOR, TINY].join('.').freeze
8
+
9
+ def self.version
10
+ VERSION
11
+ end
12
+ end
@@ -0,0 +1,35 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'csv'
4
+ require 'net/sftp'
5
+ require 'vantiv_sftp_reports/config'
6
+ require 'vantiv_sftp_reports/fetch'
7
+ require 'vantiv_sftp_reports/version'
8
+
9
+ module VantivSFTPReports
10
+ class << self
11
+ attr_reader :default_config, :default_fetch
12
+
13
+ def call(*args)
14
+ default_fetch.(*args)
15
+ end
16
+ alias fetch call
17
+
18
+ def configure(config = env_config)
19
+ @default_config = Config.with_obj(config)
20
+ @default_fetch = Fetch.new(@default_config)
21
+ end
22
+
23
+ def env_config
24
+ ENV.each_with_object({}) do |(k, v), h|
25
+ next unless k.index('vantiv_sftp_') == 0
26
+ h[k[12..-1].to_sym] = v
27
+ end
28
+ end
29
+
30
+ def first(*args)
31
+ default_fetch.first(*args)
32
+ end
33
+ end
34
+ configure
35
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vantiv_sftp_reports
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joshua
8
+ - Hansen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-06-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-sftp
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 2.1.2
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 2.1.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.16'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.16'
42
+ - !ruby/object:Gem::Dependency
43
+ name: minitest
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '5.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '5.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '10.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rubocop
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.56'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.56'
84
+ description: Report Processing for WorldPay/Vantiv/LitleOnline
85
+ email:
86
+ - joshua@epicbanality.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - LICENSE.txt
92
+ - README.md
93
+ - lib/vantiv_sftp_reports.rb
94
+ - lib/vantiv_sftp_reports/config.rb
95
+ - lib/vantiv_sftp_reports/fetch.rb
96
+ - lib/vantiv_sftp_reports/version.rb
97
+ homepage: https://github.com/binarypaladin/vantiv_sftp_reports
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 2.2.0
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.4.5.4
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Report Processing for WorldPay/Vantiv/LitleOnline
121
+ test_files: []