expressly 2.0.32.rc1

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: 765092a460c3705f8841d13a0a39acfaa5d69304
4
+ data.tar.gz: 274eed1b540c881777a709ad7c26267d40630dee
5
+ SHA512:
6
+ metadata.gz: 8c56a2753464a13dcd47c858b99fa2cbac90554a366a8da766af141a26bc2703b75f8c8a3e1f8bc2a27d22c31c84275721b0a96941da2cb138a669bb81bb2dc2
7
+ data.tar.gz: 493cbaa6101ddfda59fb10469affca648d59ca74afe0dea7645208fed21a5cf18ab3c63c32369f2ad0e8ab2486e2c21c2269b294d2d2d121c0c05976fa68719a
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,51 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ #.DS_Store
31
+
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+
41
+ # For vim:
42
+ #*.swp
43
+
44
+ # For redcar:
45
+ #.redcar
46
+
47
+ # For rubinius / eclipse:
48
+ #*.rbc
49
+ /tmp
50
+ /.buildpath
51
+ /.project
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ gem "bundler"
2
+
3
+ group :development do
4
+ require 'rbconfig'
5
+ gem 'fakeweb'
6
+ gem 'guard-rspec'
7
+ gem 'guard'
8
+ gem "rdoc", "~> 3.12"
9
+ gem 'wdm', '>= 0.1.0' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,65 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ celluloid (0.15.2)
5
+ timers (~> 1.1.0)
6
+ coderay (1.1.0)
7
+ diff-lcs (1.2.5)
8
+ ffi (1.9.3)
9
+ ffi (1.9.3-x64-mingw32)
10
+ formatador (0.2.4)
11
+ guard (2.2.4)
12
+ formatador (>= 0.2.4)
13
+ listen (~> 2.1)
14
+ lumberjack (~> 1.0)
15
+ pry (>= 0.9.12)
16
+ thor (>= 0.18.1)
17
+ guard-rspec (4.0.4)
18
+ guard (>= 2.1.1)
19
+ rspec (~> 2.14)
20
+ httpclient (2.7.0.1)
21
+ json (1.8.1)
22
+ listen (2.2.0)
23
+ celluloid (>= 0.15.2)
24
+ rb-fsevent (>= 0.9.3)
25
+ rb-inotify (>= 0.9)
26
+ lumberjack (1.0.4)
27
+ method_source (0.8.2)
28
+ pry (0.9.12.3)
29
+ coderay (~> 1.0)
30
+ method_source (~> 0.8)
31
+ slop (~> 3.4)
32
+ rb-fsevent (0.9.3)
33
+ rb-inotify (0.9.2)
34
+ ffi (>= 0.5.0)
35
+ rdoc (3.12.2)
36
+ json (~> 1.4)
37
+ rspec (2.14.1)
38
+ rspec-core (~> 2.14.0)
39
+ rspec-expectations (~> 2.14.0)
40
+ rspec-mocks (~> 2.14.0)
41
+ rspec-core (2.14.7)
42
+ rspec-expectations (2.14.4)
43
+ diff-lcs (>= 1.1.3, < 2.0)
44
+ rspec-mocks (2.14.4)
45
+ slop (3.4.7)
46
+ thor (0.18.1)
47
+ timers (1.1.0)
48
+ tzinfo (0.3.37)
49
+ wdm (0.1.1)
50
+
51
+ PLATFORMS
52
+ ruby
53
+ x64-mingw32
54
+
55
+ DEPENDENCIES
56
+ bundler
57
+ guard
58
+ guard-rspec
59
+ httpclient
60
+ rdoc (~> 3.12)
61
+ tzinfo (= 0.3.37)
62
+ wdm (>= 0.1.0)
63
+
64
+ BUNDLED WITH
65
+ 1.10.6
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { "spec"}
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Marc G. Smith
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.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = Expressly Plug-in Ruby SDK
2
+
3
+
4
+ Copyright (c) 2015 Expressly. See LICENSE.txt for further details.
5
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,79 @@
1
+ module Expressly
2
+ class ApiController < ApplicationController
3
+ skip_before_action :verify_authenticity_token
4
+
5
+ before_action :authenticate
6
+ skip_before_action :authenticate, only: [:display_popup, :migrate]
7
+
8
+ def ping
9
+ render content_type: "application/json", json: { "expressly" => "Stuff is happening!" }
10
+ end
11
+
12
+ def ping_registered
13
+ render content_type: "application/json", json: { "registered" => true }
14
+ end
15
+
16
+ def customer_export
17
+ result = Expressly::CustomerExport.new({
18
+ :metadata => config.merchant_metadata,
19
+ :primary_email => params[:email],
20
+ :customer => provider.get_customer(params[:email])
21
+ })
22
+ render content_type: "application/json", json: JSON.parse(result.to_json)
23
+ end
24
+
25
+ def invoices
26
+ result = provider.get_customer_invoices(Expressly::CustomerInvoiceRequest.from_json_list(params[:customers]))
27
+ render content_type: "application/json", json: JSON.parse(Expressly::CustomerInvoice.to_json_from_list(result))
28
+ end
29
+
30
+ def check_emails
31
+ result = provider.check_customer_statuses(params[:emails])
32
+ render content_type: "application/json", json: result
33
+ end
34
+
35
+ def display_popup
36
+ campaign_customer_uuid = params[:campaign_customer_uuid]
37
+ provider.popup_handler(self, params[:campaign_customer_uuid])
38
+ end
39
+
40
+ def migrate
41
+ campaign_customer_uuid = params[:campaign_customer_uuid]
42
+ begin
43
+
44
+ import = config.expressly_provider.fetch_customer_data(campaign_customer_uuid)
45
+ customer_reference = provider.customer_register(import.primary_email, import.customer)
46
+ provider.customer_send_password_reset_email(customer_reference)
47
+ provider.customer_update_cart(customer_reference, import.cart)
48
+ provider.customer_login(customer_reference)
49
+ redirect_to provider.customer_migrated_redirect_url(true, customer_reference)
50
+
51
+ begin
52
+ config.expressly_provider.confirm_migration_success?(campaign_customer_uuid)
53
+ rescue Exception => e
54
+ Expressly::logger.warn(self) {
55
+ "couldn't finalise migration campaign_customer_uuid=[#{campaign_customer_uuid}], email=[#{import.primary_email}], error=[#{e.message}]"
56
+ }
57
+ end
58
+ rescue Expressly::ExpresslyError
59
+ # already migrated or invalid uuid so just redirect
60
+ redirect_to provider.customer_migrated_redirect_url(false)
61
+ end
62
+ end
63
+
64
+ def authenticate
65
+ partitionedApiKey = config.api_key.partition(':')
66
+ authenticate_or_request_with_http_basic('expressly') do |username, password|
67
+ username == partitionedApiKey[0] && password == partitionedApiKey[2]
68
+ end
69
+ end
70
+
71
+ def config
72
+ Expressly::default_configuration
73
+ end
74
+
75
+ def provider
76
+ config.merchant_plugin_provider
77
+ end
78
+ end
79
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Rails.application.routes.draw do
2
+ get 'expressly/api/ping'
3
+ get 'expressly/api/ping_registered'
4
+ get 'expressly/api/user/:email' => 'expressly/api#customer_export'
5
+
6
+ post 'expressly/api/batch/invoice' => 'expressly/api#invoices'
7
+ post 'expressly/api/batch/customer' => 'expressly/api#check_emails'
8
+
9
+ get 'expressly/api/:campaign_customer_uuid/migrate' => 'expressly/api#migrate'
10
+ get 'expressly/api/:campaign_customer_uuid' => 'expressly/api#display_popup'
11
+ end
data/expressly.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "expressly/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "expressly"
7
+ s.version = Expressly::Version::STRING
8
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["Marc G. Smith"]
10
+ s.date = "2015-12-01"
11
+ s.description = "Expressly sdk to help with implementing the expressly e-commerce plug-in / module API"
12
+ s.email = "marc@buyexpressly.com"
13
+ s.extra_rdoc_files = [
14
+ "LICENSE.txt",
15
+ "README.rdoc"
16
+ ]
17
+ s.files = `git ls-files`.split("\n")
18
+
19
+ s.homepage = "http://developer.buyexpressly.com"
20
+ s.licenses = ["MIT"]
21
+ s.require_paths = ["lib"]
22
+ s.rubygems_version = "2.0.2"
23
+ s.summary = "Expressly plug-in sdk"
24
+
25
+ s.add_development_dependency(%q<bundler>, ["~> 0"])
26
+ s.add_development_dependency(%q<guard-rspec>, ["~> 0"])
27
+ s.add_development_dependency(%q<guard>, ["~> 0"])
28
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
29
+ end
data/lib/expressly.rb ADDED
@@ -0,0 +1,49 @@
1
+ module Expressly
2
+ @@logger = Logger.new(STDERR)
3
+ @@logger.level= Logger::Severity::INFO
4
+
5
+ def self.logger() @@logger end
6
+ def self.logger=(logger) @@logger = logger end
7
+
8
+ def self.default_configuration()
9
+ @@default_configuration
10
+ end
11
+
12
+ def self.default_configuration?()
13
+ !@@default_configuration.nil?
14
+ end
15
+
16
+ def self.default_configuration=(default_configuration)
17
+ @@default_configuration = default_configuration
18
+ end
19
+
20
+ class Configuration
21
+ attr_reader :api_key,
22
+ :merchant_plugin_provider, :merchant_plugin_endpoint, :merchant_metadata,
23
+ :expressly_provider, :expressly_endpoint
24
+
25
+ def initialize(api_key, merchant_plugin_provider, merchant_plugin_endpoint, merchant_metadata = {}, expressly_endpoint = 'https://prod.expresslyapp.com/api')
26
+ @api_key = api_key
27
+ @merchant_plugin_provider = merchant_plugin_provider
28
+ @merchant_plugin_endpoint = merchant_plugin_endpoint
29
+ @expressly_endpoint = expressly_endpoint
30
+ @expressly_provider = Api.new(api_key, expressly_endpoint)
31
+ @merchant_metadata = merchant_metadata
32
+ end
33
+ end
34
+ end
35
+
36
+ require 'logger'
37
+
38
+ require 'expressly/version'
39
+ require 'expressly/util'
40
+ require 'expressly/domain'
41
+ require 'expressly/api'
42
+ require 'expressly/plugin_provider'
43
+
44
+ begin
45
+ require 'expressly/engine'
46
+ rescue NameError
47
+ Expressly.logger.warn('expressly') {
48
+ "skipping loading of the expressly rails engine" }
49
+ end
@@ -0,0 +1,126 @@
1
+ module Expressly
2
+ class Api
3
+
4
+ def initialize(apikey, endpoint = 'https://prod.expresslyapp.com/api')
5
+ partitionedApiKey = apikey.partition(':')
6
+ @merchantUuid = partitionedApiKey[0]
7
+ @secretKey = partitionedApiKey[2]
8
+ @endpoint = endpoint
9
+ end
10
+
11
+ def ping?
12
+ response = execute('/v1/merchant/ping', 'GET')
13
+ return JSON.parse(response.body)['success']
14
+ end
15
+
16
+ def install(baseUrl)
17
+ apiKey = Base64.strict_encode64("#{@merchantUuid}:#{@secretKey}")
18
+ response = execute(
19
+ '/v2/plugin/merchant',
20
+ 'POST',
21
+ "{\"apiBaseUrl\":\"#{baseUrl}\", \"apiKey\":\"#{apiKey}\"}")
22
+ end
23
+
24
+ def uninstall?
25
+ response = execute(
26
+ "/v2/plugin/merchant/#{@merchantUuid}",
27
+ 'DELETE')
28
+ return JSON.parse(response.body)['success']
29
+ end
30
+
31
+ def fetch_migration_confirmation_html(campaign_customer_uuid)
32
+ response = execute(
33
+ "/v2/migration/#{campaign_customer_uuid}",
34
+ 'GET')
35
+ return response.body
36
+ end
37
+
38
+ def fetch_customer_data(campaign_customer_uuid)
39
+ response = execute(
40
+ "/v2/migration/#{campaign_customer_uuid}/user",
41
+ 'GET')
42
+ return CustomerImport.from_json(JSON.parse(response.body))
43
+ end
44
+
45
+ def confirm_migration_success?(campaign_customer_uuid)
46
+ response = execute(
47
+ "/v2/migration/#{campaign_customer_uuid}/success",
48
+ 'POST')
49
+ return JSON.parse(response.body)['success']
50
+ end
51
+
52
+ def execute(methodUri, httpVerb, body = nil)
53
+ uri = URI.parse("#{@endpoint}#{methodUri}")
54
+ http = Net::HTTP.new(uri.host, uri.port)
55
+ http.use_ssl = @endpoint.start_with?('https')
56
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
57
+
58
+ request =
59
+ if httpVerb == 'GET' then Net::HTTP::Get.new(uri.request_uri)
60
+ elsif httpVerb == 'POST' then Net::HTTP::Post.new(uri.request_uri)
61
+ elsif httpVerb == 'DELETE' then Net::HTTP::Delete.new(uri.request_uri)
62
+ else raise "unexpected http verb [#{httpVerb}]"
63
+ end
64
+
65
+ request.basic_auth(@merchantUuid, @secretKey)
66
+ if body != nil then
67
+ request["Content-Type"] = 'application/json'
68
+ request.body = body
69
+ end
70
+
71
+ response = http.request(request)
72
+
73
+ if response.code.to_i >= 300
74
+ handle_error(response)
75
+ end
76
+
77
+ return response
78
+ end
79
+
80
+ def handle_error(response)
81
+ is_json = response.content_type == "application/json"
82
+ body = is_json ? JSON.parse(response.body) : response.body
83
+
84
+ raise (if is_json && !body['id'].nil? then
85
+ ExpresslyError.new(body) else
86
+ HttpError.new(response) end)
87
+ end
88
+
89
+ private :execute, :handle_error
90
+ end
91
+
92
+ class HttpError < StandardError
93
+ attr_accessor :code, :body
94
+ def initialize(response)
95
+ super(response.message)
96
+ @code = response.code
97
+ @body = response.body
98
+ self.freeze
99
+ end
100
+
101
+ private :initialize
102
+
103
+ end
104
+
105
+ class ExpresslyError < StandardError
106
+ attr_accessor :id, :code, :description, :causes, :actions
107
+ def initialize(response)
108
+ super(response['message'])
109
+ @id = response['id']
110
+ @code = response['code']
111
+ @description = response['description']
112
+ @causes = response['causes']
113
+ @actions = response['actions']
114
+ self.freeze
115
+ end
116
+ end
117
+
118
+ private :initialize
119
+
120
+ end
121
+
122
+ require "net/http"
123
+ require "net/https"
124
+ require "uri"
125
+ require 'json'
126
+ require 'base64'