parking_ticket 1.0.32 → 1.0.40
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 +4 -4
- data/Gemfile +7 -0
- data/Gemfile.lock +17 -1
- data/lib/clients/adapter.rb +64 -0
- data/lib/clients/pay_by_phone/adapter.rb +95 -0
- data/lib/clients/pay_by_phone/client.rb +157 -0
- data/lib/parking_ticket/version.rb +1 -1
- data/lib/parking_ticket.rb +26 -29
- metadata +5 -5
- data/lib/client/pay_by_phone/adapter.rb +0 -71
- data/lib/client/pay_by_phone/configuration.rb +0 -9
- data/lib/client/pay_by_phone/request.rb +0 -133
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a45be3c94ffccf5e5e1f66abf01a80528a5f9ab95d22b7bbd281996889b3cd5f
|
4
|
+
data.tar.gz: 52609368928c5d70bc382619b39bf2962209be2e0456bc90a4e0d89dfb3f0954
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50c8f9a60ea1f075914e468fbb7057997397a2fd800003321e0f4e03a4e1efbcfc11b6352b6b3d508b1ac95cd5b823738385b8b8c31c298179cb7a7307a9fa02
|
7
|
+
data.tar.gz: 5bc40023e17ed5235bccdbddd27d3e78e4cdea6c7e43ecc58397e81e85be5637ce5418106ffb0ab4f8c447660b65e19312a3da7f0d2979c4d14d00eaff2184a8
|
data/Gemfile
CHANGED
@@ -9,6 +9,13 @@ gem 'rake', '~> 13.0'
|
|
9
9
|
|
10
10
|
gem 'rspec', '~> 3.0'
|
11
11
|
|
12
|
+
# VCR, save test suite's http requests and replay them
|
13
|
+
gem 'vcr', '~> 6.0'
|
14
|
+
# Mock web requests in tests
|
15
|
+
gem 'webmock', '~> 3.12'
|
16
|
+
|
17
|
+
gem 'byebug'
|
18
|
+
|
12
19
|
gem 'faraday', '~> 2.7', '>= 2.7.2'
|
13
20
|
|
14
21
|
gem 'json_matchers'
|
data/Gemfile.lock
CHANGED
@@ -1,21 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
parking_ticket (1.0.
|
4
|
+
parking_ticket (1.0.40)
|
5
5
|
faraday
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
+
addressable (2.8.1)
|
11
|
+
public_suffix (>= 2.0.2, < 6.0)
|
12
|
+
byebug (11.1.3)
|
13
|
+
crack (0.4.5)
|
14
|
+
rexml
|
10
15
|
diff-lcs (1.5.0)
|
11
16
|
faraday (2.7.2)
|
12
17
|
faraday-net_http (>= 2.0, < 3.1)
|
13
18
|
ruby2_keywords (>= 0.0.4)
|
14
19
|
faraday-net_http (3.0.2)
|
20
|
+
hashdiff (1.0.1)
|
15
21
|
json_matchers (0.11.1)
|
16
22
|
json_schema
|
17
23
|
json_schema (0.21.0)
|
24
|
+
public_suffix (5.0.1)
|
18
25
|
rake (13.0.6)
|
26
|
+
rexml (3.2.5)
|
19
27
|
rspec (3.12.0)
|
20
28
|
rspec-core (~> 3.12.0)
|
21
29
|
rspec-expectations (~> 3.12.0)
|
@@ -30,17 +38,25 @@ GEM
|
|
30
38
|
rspec-support (~> 3.12.0)
|
31
39
|
rspec-support (3.12.0)
|
32
40
|
ruby2_keywords (0.0.5)
|
41
|
+
vcr (6.1.0)
|
42
|
+
webmock (3.18.1)
|
43
|
+
addressable (>= 2.8.0)
|
44
|
+
crack (>= 0.3.2)
|
45
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
33
46
|
|
34
47
|
PLATFORMS
|
35
48
|
arm64-darwin-20
|
36
49
|
x86_64-linux
|
37
50
|
|
38
51
|
DEPENDENCIES
|
52
|
+
byebug
|
39
53
|
faraday (~> 2.7, >= 2.7.2)
|
40
54
|
json_matchers
|
41
55
|
parking_ticket!
|
42
56
|
rake (~> 13.0)
|
43
57
|
rspec (~> 3.0)
|
58
|
+
vcr (~> 6.0)
|
59
|
+
webmock (~> 3.12)
|
44
60
|
|
45
61
|
BUNDLED WITH
|
46
62
|
2.2.32
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'byebug'
|
2
|
+
module ParkingTicket
|
3
|
+
module Clients
|
4
|
+
class Adapter
|
5
|
+
class << self
|
6
|
+
def parent_module
|
7
|
+
const_get(name.split('::')[0..-2].join('::'))
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid_credentials?(username, password)
|
11
|
+
parent_module::Client.auth(username, password).status == 200
|
12
|
+
end
|
13
|
+
end
|
14
|
+
class Error < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(username, password)
|
18
|
+
@username = username
|
19
|
+
@password = password
|
20
|
+
end
|
21
|
+
|
22
|
+
def vehicles
|
23
|
+
raise_invalid_credentials! unless valid_credentials?
|
24
|
+
fetch_and_map_vehicles
|
25
|
+
end
|
26
|
+
|
27
|
+
def rate_options(zipcode, license_plate)
|
28
|
+
raise_invalid_credentials! unless valid_credentials?
|
29
|
+
fetch_and_map_rate_options(zipcode, license_plate)
|
30
|
+
end
|
31
|
+
|
32
|
+
def running_ticket(license_plate, zipcode)
|
33
|
+
raise_invalid_credentials! unless valid_credentials?
|
34
|
+
fetch_and_map_running_ticket(license_plate, zipcode)
|
35
|
+
end
|
36
|
+
|
37
|
+
def payment_methods
|
38
|
+
raise_invalid_credentials! unless valid_credentials?
|
39
|
+
fetch_and_map_payment_methods
|
40
|
+
end
|
41
|
+
|
42
|
+
def new_ticket(license_plate, zipcode, rate_option_id, quantity, time_unit, payment_method_id)
|
43
|
+
raise_invalid_credentials! unless valid_credentials?
|
44
|
+
return if running_ticket(license_plate, zipcode)
|
45
|
+
|
46
|
+
request_new_ticket(license_plate, zipcode, rate_option_id, quantity, time_unit, payment_method_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def client
|
52
|
+
@client ||= self.class.parent_module::Client.new(@username, @password)
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid_credentials?
|
56
|
+
@valid_credentials ||= self.class.valid_credentials?(@username, @password)
|
57
|
+
end
|
58
|
+
|
59
|
+
def raise_invalid_credentials!
|
60
|
+
raise Error, 'Adapter credentials are not valid'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module ParkingTicket
|
2
|
+
module Clients
|
3
|
+
module PayByPhone
|
4
|
+
class Adapter < Clients::Adapter
|
5
|
+
ACCEPTED_TIME_UNIT_MAPPER = {
|
6
|
+
'Days' => 'days',
|
7
|
+
'Minutes' => 'minutes',
|
8
|
+
'Hours' => 'hours'
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
PAYMENT_CARD_TYPE_MAPPER = {
|
12
|
+
'MasterCard' => 'master_card',
|
13
|
+
'Visa' => 'visa'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def fetch_and_map_vehicles
|
19
|
+
client.vehicles.map do |vehicle|
|
20
|
+
{
|
21
|
+
client_internal_id: vehicle['vehicleId'],
|
22
|
+
license_plate: vehicle['licensePlate'],
|
23
|
+
type: vehicle['type']
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_and_map_rate_options(zipcode, license_plate)
|
29
|
+
client.rate_options(zipcode, license_plate).map do |rate_option|
|
30
|
+
mapped_time_units = rate_option['acceptedTimeUnits'].map do |accepted_time_unit|
|
31
|
+
ACCEPTED_TIME_UNIT_MAPPER[accepted_time_unit]
|
32
|
+
end
|
33
|
+
{
|
34
|
+
client_internal_id: rate_option['rateOptionId'],
|
35
|
+
name: rate_option['name'],
|
36
|
+
type: rate_option['type'],
|
37
|
+
accepted_time_units: mapped_time_units
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch_and_map_running_ticket(license_plate, zipcode)
|
43
|
+
client.running_tickets.filter do |ticket|
|
44
|
+
ticket.dig('vehicle', 'licensePlate') == license_plate && ticket['locationId'] == zipcode
|
45
|
+
end.map do |ticket|
|
46
|
+
{
|
47
|
+
client_internal_id: ticket['parkingSessionId'],
|
48
|
+
starts_on: DateTime.parse(ticket['startTime']),
|
49
|
+
ends_on: DateTime.parse(ticket['expireTime']),
|
50
|
+
license_plate: ticket.dig('vehicle', 'licensePlate'),
|
51
|
+
cost: ticket.dig('segments', 0, 'cost'),
|
52
|
+
client: 'PayByPhone'
|
53
|
+
}
|
54
|
+
end.first
|
55
|
+
end
|
56
|
+
|
57
|
+
def request_new_ticket(license_plate, zipcode, rate_option_id, quantity, time_unit, payment_method_id)
|
58
|
+
mapped_time_unit = ACCEPTED_TIME_UNIT_MAPPER.key(time_unit)
|
59
|
+
payment_method = fetch_and_map_payment_methods.find do |payment_method|
|
60
|
+
payment_method[:client_internal_id] == payment_method_id
|
61
|
+
end
|
62
|
+
quote = fetch_and_map_quote(rate_option_id, zipcode, license_plate, quantity, mapped_time_unit)
|
63
|
+
client.new_ticket(
|
64
|
+
quote[:client_internal_id],
|
65
|
+
payment_method[:client_internal_id],
|
66
|
+
zipcode,
|
67
|
+
license_plate,
|
68
|
+
quantity,
|
69
|
+
mapped_time_unit,
|
70
|
+
quote[:starts_on]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def fetch_and_map_payment_methods
|
75
|
+
client.payment_methods['items'].map do |payment_method|
|
76
|
+
{
|
77
|
+
client_internal_id: payment_method['id'],
|
78
|
+
anonymised_card_number: payment_method['maskedCardNumber'][-4..],
|
79
|
+
payment_card_type: PAYMENT_CARD_TYPE_MAPPER[payment_method['paymentCardType']]
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def fetch_and_map_quote(rate_option_id, zipcode, license_plate, quantity, time_unit)
|
85
|
+
fetched_quote = client.quote(rate_option_id, zipcode, license_plate, quantity, time_unit)
|
86
|
+
{
|
87
|
+
client_internal_id: fetched_quote['quoteId'],
|
88
|
+
starts_on: fetched_quote['parkingStartTime'],
|
89
|
+
ends_on: fetched_quote['parkingExpiryTime']
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
module ParkingTicket
|
3
|
+
module Clients
|
4
|
+
module PayByPhone
|
5
|
+
class Client
|
6
|
+
class << self
|
7
|
+
def rate_options(token, account_id, zipcode, license_plate)
|
8
|
+
connection(token).get("/parking/locations/#{zipcode}/rateOptions",
|
9
|
+
{
|
10
|
+
parkingAccountId: account_id,
|
11
|
+
licensePlate: license_plate
|
12
|
+
}).body
|
13
|
+
end
|
14
|
+
|
15
|
+
def vehicles(token)
|
16
|
+
connection(token).get('/identity/profileservice/v1/members/vehicles/paybyphone').body
|
17
|
+
end
|
18
|
+
|
19
|
+
def auth(username, password)
|
20
|
+
conn = Faraday.new('https://auth.paybyphoneapis.com') do |f|
|
21
|
+
f.response :json
|
22
|
+
end
|
23
|
+
conn.post(
|
24
|
+
'/token',
|
25
|
+
URI.encode_www_form({
|
26
|
+
grant_type: 'password',
|
27
|
+
username: username,
|
28
|
+
password: password,
|
29
|
+
client_id: 'paybyphone_web'
|
30
|
+
}),
|
31
|
+
{
|
32
|
+
'Accept' => 'application/json, text/plain, */*',
|
33
|
+
'X-Pbp-ClientType' => 'WebApp'
|
34
|
+
}
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def account_id(token)
|
39
|
+
connection(token).get('/parking/accounts').body.dig(0, 'id')
|
40
|
+
end
|
41
|
+
|
42
|
+
def running_tickets(token, account_id)
|
43
|
+
connection(token).get("/parking/accounts/#{account_id}/sessions?periodType=Current").body
|
44
|
+
end
|
45
|
+
|
46
|
+
def quote(token, account_id, rate_option_id, zipcode, license_plate, quantity, time_unit)
|
47
|
+
connection(token).get(
|
48
|
+
"/parking/accounts/#{account_id}/quote",
|
49
|
+
{
|
50
|
+
locationId: zipcode,
|
51
|
+
licensePlate: license_plate,
|
52
|
+
rateOptionId: rate_option_id,
|
53
|
+
durationTimeUnit: time_unit,
|
54
|
+
durationQuantity: quantity,
|
55
|
+
isParkUntil: false,
|
56
|
+
parkingAccountId: account_id
|
57
|
+
}
|
58
|
+
).body
|
59
|
+
end
|
60
|
+
|
61
|
+
def new_ticket(token, account_id, quote_id, payment_method_id, zipcode, license_plate, quantity, time_unit, start_time)
|
62
|
+
connection(token).post(
|
63
|
+
"/parking/accounts/#{account_id}/sessions/",
|
64
|
+
{
|
65
|
+
"expireTime": nil,
|
66
|
+
"duration": {
|
67
|
+
"quantity": quantity,
|
68
|
+
"timeUnit": time_unit
|
69
|
+
},
|
70
|
+
"licensePlate": license_plate,
|
71
|
+
"locationId": zipcode,
|
72
|
+
"rateOptionId": '75101',
|
73
|
+
"startTime": start_time,
|
74
|
+
"quoteId": quote_id,
|
75
|
+
"parkingAccountId": account_id,
|
76
|
+
"paymentMethod": {
|
77
|
+
"paymentMethodType": 'PaymentAccount',
|
78
|
+
"payload": {
|
79
|
+
"paymentAccountId": payment_method_id,
|
80
|
+
"clientBrowserDetails": {
|
81
|
+
"browserAcceptHeader": 'text/html',
|
82
|
+
"browserColorDepth": '30',
|
83
|
+
"browserJavaEnabled": 'false',
|
84
|
+
"browserLanguage": 'fr-FR',
|
85
|
+
"browserScreenHeight": '900',
|
86
|
+
"browserScreenWidth": '1440',
|
87
|
+
"browserTimeZone": '-60',
|
88
|
+
"browserUserAgent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}.to_json
|
93
|
+
).body
|
94
|
+
end
|
95
|
+
|
96
|
+
def payment_methods(token)
|
97
|
+
connection(token).get('/payment/v3/accounts').body
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def connection(token)
|
103
|
+
Faraday.new(
|
104
|
+
url: 'https://consumer.paybyphoneapis.com',
|
105
|
+
headers: {
|
106
|
+
'Content-Type' => 'application/json',
|
107
|
+
'Authorization' => "Bearer #{token}"
|
108
|
+
}
|
109
|
+
) do |f|
|
110
|
+
f.response :json
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def initialize(username, password)
|
116
|
+
@username = username
|
117
|
+
@password = password
|
118
|
+
end
|
119
|
+
|
120
|
+
def vehicles
|
121
|
+
self.class.vehicles(token)
|
122
|
+
end
|
123
|
+
|
124
|
+
def rate_options(zipcode, license_plate)
|
125
|
+
self.class.rate_options(token, account_id, zipcode, license_plate)
|
126
|
+
end
|
127
|
+
|
128
|
+
def running_tickets
|
129
|
+
self.class.running_tickets(token, account_id)
|
130
|
+
end
|
131
|
+
|
132
|
+
def payment_methods
|
133
|
+
self.class.payment_methods(token)
|
134
|
+
end
|
135
|
+
|
136
|
+
def quote(rate_option_id, zipcode, license_plate, quantity, time_unit)
|
137
|
+
self.class.quote(token, account_id, rate_option_id, zipcode, license_plate, quantity, time_unit)
|
138
|
+
end
|
139
|
+
|
140
|
+
def new_ticket(quote_id, payment_method_id, zipcode, license_plate, quantity, time_unit, start_time)
|
141
|
+
self.class.new_ticket(token, account_id, quote_id, payment_method_id, zipcode, license_plate, quantity,
|
142
|
+
time_unit, start_time)
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def token
|
148
|
+
@token ||= self.class.auth(@username, @password).body['access_token']
|
149
|
+
end
|
150
|
+
|
151
|
+
def account_id
|
152
|
+
@account_id ||= self.class.account_id(token)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/parking_ticket.rb
CHANGED
@@ -4,15 +4,15 @@ require 'parking_ticket/configuration'
|
|
4
4
|
require 'parking_ticket/version'
|
5
5
|
|
6
6
|
# PayByPhone wrapper
|
7
|
-
require '
|
8
|
-
require '
|
9
|
-
require '
|
7
|
+
require 'clients/adapter'
|
8
|
+
require 'clients/pay_by_phone/adapter'
|
9
|
+
require 'clients/pay_by_phone/client'
|
10
10
|
|
11
11
|
module ParkingTicket
|
12
12
|
class Base
|
13
13
|
class << self
|
14
14
|
def valid_credentials?(adapter_name, username, password)
|
15
|
-
adapter =
|
15
|
+
adapter = Clients::PayByPhone::Adapter if adapter_name == 'pay_by_phone'
|
16
16
|
raise Error, 'EasyPark will be handled in the next major release' if adapter_name == 'easy_park'
|
17
17
|
raise Error, "Unhandled adapter : #{adapter_name}" unless adapter
|
18
18
|
|
@@ -44,24 +44,30 @@ module ParkingTicket
|
|
44
44
|
class Error < StandardError
|
45
45
|
end
|
46
46
|
|
47
|
-
def initialize(adapter_name,
|
47
|
+
def initialize(adapter_name, username, password)
|
48
48
|
@adapter_name = adapter_name
|
49
|
-
@
|
50
|
-
@
|
49
|
+
@username = username
|
50
|
+
@password = password
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
54
|
-
|
53
|
+
def vehicles
|
54
|
+
adapter.vehicles
|
55
|
+
end
|
56
|
+
|
57
|
+
def rate_options(zipcode, license_plate)
|
58
|
+
adapter.rate_options(zipcode, license_plate)
|
55
59
|
end
|
56
60
|
|
57
|
-
def
|
58
|
-
adapter.
|
61
|
+
def running_ticket(license_plate, zipcode)
|
62
|
+
adapter.running_ticket(license_plate, zipcode)
|
59
63
|
end
|
60
64
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
def payment_methods
|
66
|
+
adapter.payment_methods
|
67
|
+
end
|
68
|
+
|
69
|
+
def new_ticket(license_plate, zipcode, rate_option_id, quantity, time_unit, payment_method_id)
|
70
|
+
adapter.new_ticket(license_plate, zipcode, rate_option_id, quantity, time_unit, payment_method_id)
|
65
71
|
end
|
66
72
|
|
67
73
|
private
|
@@ -74,24 +80,15 @@ module ParkingTicket
|
|
74
80
|
end
|
75
81
|
|
76
82
|
def prepare_pay_by_phone_adapter!
|
77
|
-
|
78
|
-
return Client::PayByPhone::Adapter.new(configuration) if configuration.completed?
|
79
|
-
|
80
|
-
raise Error, 'Uncompleted configuration'
|
81
|
-
end
|
82
|
-
|
83
|
-
def pay_by_phone_configuration
|
84
|
-
Client::PayByPhone::Configuration.new do |config|
|
85
|
-
config.username = @configuration_attributes[:username]
|
86
|
-
config.password = @configuration_attributes[:password]
|
87
|
-
config.license_plate = @configuration_attributes[:license_plate]
|
88
|
-
config.zipcode = @configuration_attributes[:zipcode]
|
89
|
-
config.card_number = @configuration_attributes[:card_number]
|
90
|
-
end
|
83
|
+
Clients::PayByPhone::Adapter.new(@username, @password)
|
91
84
|
end
|
92
85
|
|
93
86
|
def prepare_easy_park_adapter!
|
94
87
|
raise Error, 'EasyPark will be handled in the next major release'
|
95
88
|
end
|
89
|
+
|
90
|
+
def adapter
|
91
|
+
@adapter ||= load_adapter!
|
92
|
+
end
|
96
93
|
end
|
97
94
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parking_ticket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.40
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Ecrepont
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -40,9 +40,9 @@ files:
|
|
40
40
|
- Rakefile
|
41
41
|
- bin/console
|
42
42
|
- bin/setup
|
43
|
-
- lib/
|
44
|
-
- lib/
|
45
|
-
- lib/
|
43
|
+
- lib/clients/adapter.rb
|
44
|
+
- lib/clients/pay_by_phone/adapter.rb
|
45
|
+
- lib/clients/pay_by_phone/client.rb
|
46
46
|
- lib/parking_ticket.rb
|
47
47
|
- lib/parking_ticket/configuration.rb
|
48
48
|
- lib/parking_ticket/version.rb
|
@@ -1,71 +0,0 @@
|
|
1
|
-
module ParkingTicket
|
2
|
-
module Client
|
3
|
-
module PayByPhone
|
4
|
-
class Adapter
|
5
|
-
class << self
|
6
|
-
def valid_credentials?(username, password)
|
7
|
-
Request.valid_credentials?(username, password)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(configuration)
|
12
|
-
@configuration = configuration
|
13
|
-
end
|
14
|
-
|
15
|
-
def valid_credentials?(_username, _password)
|
16
|
-
request.valid_credentials?(_username, _password)
|
17
|
-
end
|
18
|
-
|
19
|
-
def covered?
|
20
|
-
!!current_ticket
|
21
|
-
end
|
22
|
-
|
23
|
-
def current_ticket
|
24
|
-
ticket = request.tickets(account_id).find do |ticket|
|
25
|
-
ticket.dig('vehicle', 'licensePlate') == @configuration.license_plate
|
26
|
-
end
|
27
|
-
|
28
|
-
return unless ticket
|
29
|
-
|
30
|
-
{
|
31
|
-
starts_on: DateTime.parse(ticket['startTime']),
|
32
|
-
ends_on: DateTime.parse(ticket['expireTime']),
|
33
|
-
license_plate: ticket.dig('vehicle', 'licensePlate'),
|
34
|
-
cost: ticket.dig('segments', 0, 'cost'),
|
35
|
-
client: 'PayByPhone',
|
36
|
-
client_ticket_id: ticket['parkingSessionId']
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
def renew
|
41
|
-
return if covered?
|
42
|
-
|
43
|
-
quote = request.new_quote(rate_option_id, account_id)
|
44
|
-
request.new_ticket(account_id, quote['parkingStartTime'], quote['quoteId'], payment_method_id)
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def request
|
50
|
-
@request ||= Request.new(@configuration)
|
51
|
-
end
|
52
|
-
|
53
|
-
def account_id
|
54
|
-
@account_id ||= request.accounts.dig(0, 'id')
|
55
|
-
end
|
56
|
-
|
57
|
-
def payment_method_id
|
58
|
-
request.payment_methods['items'].find do |payment_method|
|
59
|
-
payment_method['maskedCardNumber'] == @configuration.card_number
|
60
|
-
end['id']
|
61
|
-
end
|
62
|
-
|
63
|
-
def rate_option_id
|
64
|
-
request.rate_options(account_id).find do |rate_option|
|
65
|
-
rate_option['type'] == 'RES' && rate_option['licensePlate'] == @configuration.license_plate
|
66
|
-
end['rateOptionId']
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,133 +0,0 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
module ParkingTicket
|
3
|
-
module Client
|
4
|
-
module PayByPhone
|
5
|
-
class Request
|
6
|
-
class << self
|
7
|
-
def valid_credentials?(username, password)
|
8
|
-
auth(username, password).status == 200
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def auth(username, password)
|
14
|
-
conn = Faraday.new('https://auth.paybyphoneapis.com') do |f|
|
15
|
-
f.response :json
|
16
|
-
end
|
17
|
-
conn.post(
|
18
|
-
'/token',
|
19
|
-
URI.encode_www_form({
|
20
|
-
grant_type: 'password',
|
21
|
-
username: username,
|
22
|
-
password: password,
|
23
|
-
client_id: 'paybyphone_web'
|
24
|
-
}),
|
25
|
-
{
|
26
|
-
'Accept' => 'application/json, text/plain, */*',
|
27
|
-
'X-Pbp-ClientType' => 'WebApp'
|
28
|
-
}
|
29
|
-
)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize(configuration)
|
34
|
-
@configuration = configuration
|
35
|
-
end
|
36
|
-
|
37
|
-
def tickets(account_id)
|
38
|
-
connection.get("/parking/accounts/#{account_id}/sessions?periodType=Current").body
|
39
|
-
end
|
40
|
-
|
41
|
-
def new_ticket(account_id, parking_start_time, quote_id, payment_method_id)
|
42
|
-
connection.post(
|
43
|
-
"/parking/accounts/#{account_id}/sessions/",
|
44
|
-
{
|
45
|
-
"expireTime": nil,
|
46
|
-
"duration": {
|
47
|
-
"quantity": '1',
|
48
|
-
"timeUnit": 'days'
|
49
|
-
},
|
50
|
-
"licensePlate": @configuration.license_plate,
|
51
|
-
"locationId": @configuration.zipcode,
|
52
|
-
"rateOptionId": '75101',
|
53
|
-
"startTime": parking_start_time,
|
54
|
-
"quoteId": quote_id,
|
55
|
-
"parkingAccountId": account_id,
|
56
|
-
"paymentMethod": {
|
57
|
-
"paymentMethodType": 'PaymentAccount',
|
58
|
-
"payload": {
|
59
|
-
"paymentAccountId": payment_method_id,
|
60
|
-
"clientBrowserDetails": {
|
61
|
-
"browserAcceptHeader": 'text/html',
|
62
|
-
"browserColorDepth": '30',
|
63
|
-
"browserJavaEnabled": 'false',
|
64
|
-
"browserLanguage": 'fr-FR',
|
65
|
-
"browserScreenHeight": '900',
|
66
|
-
"browserScreenWidth": '1440',
|
67
|
-
"browserTimeZone": '-60',
|
68
|
-
"browserUserAgent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
|
69
|
-
}
|
70
|
-
}
|
71
|
-
}
|
72
|
-
}.to_json
|
73
|
-
).body
|
74
|
-
end
|
75
|
-
|
76
|
-
def accounts
|
77
|
-
connection.get('/parking/accounts').body
|
78
|
-
end
|
79
|
-
|
80
|
-
def vehicles
|
81
|
-
connection.get('/identity/profileservice/v1/members/vehicles/paybyphone').body
|
82
|
-
end
|
83
|
-
|
84
|
-
def rate_options(account_id)
|
85
|
-
connection.get("/parking/locations/#{@configuration.zipcode}/rateOptions",
|
86
|
-
{
|
87
|
-
parkingAccountId: account_id,
|
88
|
-
licensePlate: @configuration.license_plate
|
89
|
-
}).body
|
90
|
-
end
|
91
|
-
|
92
|
-
def new_quote(rate_option_id, account_id)
|
93
|
-
connection.get(
|
94
|
-
"/parking/accounts/#{account_id}/quote",
|
95
|
-
{
|
96
|
-
locationId: @configuration.zipcode,
|
97
|
-
licensePlate: @configuration.license_plate,
|
98
|
-
rateOptionId: rate_option_id,
|
99
|
-
durationTimeUnit: 'Days',
|
100
|
-
durationQuantity: 1,
|
101
|
-
isParkUntil: false,
|
102
|
-
parkingAccountId: account_id
|
103
|
-
}
|
104
|
-
).body
|
105
|
-
end
|
106
|
-
|
107
|
-
def member
|
108
|
-
connection.get('/identity/profileservice/v1/members').body
|
109
|
-
end
|
110
|
-
|
111
|
-
def payment_methods
|
112
|
-
connection.get('/payment/v3/accounts').body
|
113
|
-
end
|
114
|
-
|
115
|
-
def connection
|
116
|
-
@connection ||= Faraday.new(
|
117
|
-
url: 'https://consumer.paybyphoneapis.com',
|
118
|
-
headers: {
|
119
|
-
'Content-Type' => 'application/json',
|
120
|
-
'Authorization' => "Bearer #{token}"
|
121
|
-
}
|
122
|
-
) do |f|
|
123
|
-
f.response :json
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def token
|
128
|
-
self.class.send(:auth, @configuration.username, @configuration.password).body['access_token']
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|