# typed: true # frozen_string_literal: true require 'faraday' module ParkingTicket module Clients module PayByPhone class Client extend T::Sig class << self extend T::Sig sig { params(username: String, password: String).returns(Faraday::Response) } def auth(username, password) conn = Faraday.new('https://auth.paybyphoneapis.com') do |f| f.response :json f.response :raise_error end conn.post( '/token', URI.encode_www_form({ grant_type: 'password', username: username, password: password, client_id: 'paybyphone_web' }), { 'Accept' => 'application/json, text/plain, */*', 'X-Pbp-ClientType' => 'WebApp' } ) end sig { params(token: String, account_id: String, zipcode: String, license_plate: String).returns(Array) } def rate_options(token, account_id, zipcode, license_plate) connection(token).get("/parking/locations/#{zipcode}/rateOptions", { parkingAccountId: account_id, licensePlate: license_plate }).body end sig { params(token: String).returns(T::Array[T::Hash[String, T.untyped]]) } def vehicles(token) connection(token).get('/identity/profileservice/v1/members/vehicles/paybyphone').body end sig { params(token: String).returns(String) } def account_id(token) connection(token).get('/parking/accounts').body.dig(0, 'id') end sig { params(token: String, account_id: String).returns(T::Array[T::Hash[String, T.untyped]]) } def running_tickets(token, account_id) connection(token).get("/parking/accounts/#{account_id}/sessions?periodType=Current").body end sig do params( token: String, account_id: String, rate_option_id: String, zipcode: String, license_plate: String, quantity: Integer, time_unit: String ).returns(T::Hash[String, T.untyped]) end def quote(token, account_id, rate_option_id, zipcode, license_plate, quantity, time_unit) connection(token).get( "/parking/accounts/#{account_id}/quote", { locationId: zipcode, licensePlate: license_plate, rateOptionId: rate_option_id, durationTimeUnit: time_unit, durationQuantity: quantity, isParkUntil: false, parkingAccountId: account_id } ).body end sig do params( token: String, account_id: String, license_plate: String, zipcode: String, rate_option_client_internal_id: String, quantity: Integer, time_unit: String, quote_client_internal_id: String, starts_on: DateTime, payment_method_id: T.nilable(String) ).void end def new_ticket(token, account_id, license_plate:, zipcode:, rate_option_client_internal_id:, quantity:, time_unit:, quote_client_internal_id:, starts_on:, payment_method_id:) base_data = { expireTime: nil, duration: { quantity: quantity.to_s, timeUnit: time_unit }, licensePlate: license_plate, locationId: zipcode, rateOptionId: rate_option_client_internal_id, startTime: starts_on.to_s, quoteId: quote_client_internal_id, parkingAccountId: account_id } payment_data = { paymentMethod: { paymentMethodType: 'PaymentAccount', payload: { paymentAccountId: payment_method_id, clientBrowserDetails: { browserAcceptHeader: 'text/html', browserColorDepth: '30', browserJavaEnabled: 'false', browserLanguage: 'fr-FR', browserScreenHeight: '900', browserScreenWidth: '1440', browserTimeZone: '-60', 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' } } } } final_data = payment_method_id ? base_data.merge(payment_data) : base_data connection(token).post( "/parking/accounts/#{account_id}/sessions/", JSON.generate(final_data) ).body end sig { params(token: String).returns(T::Hash[String, T::Array[T::Hash[String, T.untyped]]]) } def payment_methods(token) connection = Faraday.new( url: 'https://payments.paybyphoneapis.com', headers: { accept: 'application/json, text/plain, */*', 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7', authorization: "Bearer #{token}", 'content-type': 'application/json', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'cross-site', 'x-api-key': 'zZ4ePLvoBBD1YwBGCo6P5DiPLDjSss3j', 'x-pbp-clienttype': 'WebApp', Referer: 'https://m2.paybyphone.fr/', 'Referrer-Policy': 'strict-origin-when-cross-origin' } ) do |f| f.response :json f.response :raise_error end connection.get('/v1/accounts').body end private sig { params(token: String, subdomain: T.nilable(String)).returns(Faraday::Connection) } def connection(token, subdomain: 'consumer') Faraday.new( url: "https://#{subdomain}.paybyphoneapis.com", headers: { 'Content-Type' => 'application/json', 'Authorization' => "Bearer #{token}", 'Referer' => 'https://m2.paybyphone.fr/' } ) do |f| f.response :json f.response :raise_error end end end def initialize(username, password) @username = username @password = password end sig { returns(T::Array[T::Hash[String, T.untyped]]) } def vehicles self.class.vehicles(token) end sig { params(zipcode: String, license_plate: String).returns(T::Array[T::Hash[String, T.untyped]]) } def rate_options(zipcode, license_plate) self.class.rate_options(token, account_id, zipcode, license_plate) end sig { returns(T::Array[T::Hash[String, T.untyped]]) } def running_tickets self.class.running_tickets(token, account_id) end sig { returns(T::Hash[String, T::Array[T::Hash[String, T.untyped]]]) } def payment_methods self.class.payment_methods(token) end sig do params(rate_option_id: String, zipcode: String, license_plate: String, quantity: Integer, time_unit: String).returns(T::Hash[String, T.untyped]) end def quote(rate_option_id, zipcode, license_plate, quantity, time_unit) self.class.quote(token, account_id, rate_option_id, zipcode, license_plate, quantity, time_unit) end sig do params( license_plate: String, zipcode: String, rate_option_client_internal_id: String, quantity: Integer, time_unit: String, quote_client_internal_id: String, starts_on: DateTime, payment_method_id: T.nilable(String) ) .void end def new_ticket(license_plate:, zipcode:, rate_option_client_internal_id:, quantity:, time_unit:, quote_client_internal_id:, starts_on:, payment_method_id:) self.class.new_ticket(token, account_id, license_plate: license_plate, zipcode: zipcode, rate_option_client_internal_id: rate_option_client_internal_id, quantity: quantity, time_unit: time_unit, quote_client_internal_id: quote_client_internal_id, starts_on: starts_on, payment_method_id: payment_method_id) end private sig { returns(String) } def token @token ||= self.class.auth(@username, @password).body['access_token'] end sig { returns(String) } def account_id @account_id ||= self.class.account_id(token) end end end end end