lib/sailplay/client.rb
require 'rest-client'
require 'multi_json'
require 'sailplay/configuration'
require 'sailplay/error'
require 'sailplay/response'
module Sailplay
class Client
attr_reader :protocol,
:host,
:port,
:endpoint,
:secure,
:connection_options,
:store_id,
:store_key,
:store_pin
alias_method :secure?, :secure
def initialize(options = {})
[ :protocol,
:host,
:port,
:endpoint,
:secure,
:connection_options,
:store_id,
:store_key,
:store_pin
].each do |option|
instance_variable_set("@#{option}", options[option])
end
end
# @param [String] phone
# @param [Hash] options
#
# @option options [true|false] :auth — authenticate user
#
# @return [Sailplay::User]
def create_user(phone, options = {})
params = {:user_phone => phone}
params[:extra_fields] = 'auth_hash' if options[:auth]
response = request(:get, '/v1/users/reg', :user_phone => phone)
if response.success?
User.parse(response.data)
else
raise APIError, "Cannot create user '#{phone}': #{response.error_message}"
end
end
# @param [String] user_id
# @param [Hash] options
#
# @option options [true|false] :auth — authenticate user
#
# @return [Sailplay::User]
def find_user(user_id, options = {})
params = {:origin_user_id => user_id}
params[:user_phone] = options[:phone] if options[:phone]
params[:extra_fields] = 'auth_hash' if options[:auth]
response = request(:get, '/v1/users/points-info', params)
if response.success?
User.parse(response.data)
else
raise APIError, "Cannot find a user '#{phone}': #{response.error_message}"
end
end
# options[:points_rate] — коэффициент конвертации рублей в баллы. Может принимать значение из полуинтервала (0,1].
# При отсутствии данного параметра, используется значение, указанное в настройках.
# Формат points_rate=0.45
# options[:force_complete] — если true, транзакция считается подтвержденной несмотря на флаг в настройках.
# Данный аттрибут может быть использован, например, в случае когда часть оплат
# необходимо подтверждать, а про остальные известно что они уже подтверждены.
# options[:order_id] — ID заказа
#
# @return [Sailplay::Purchase]
def create_purchase(user_id, price, options = {})
params = {:price => price, :origin_user_id => user_id}
params[:user_phone] = options[:phone] if options[:phone]
params[:points_rate] = options[:points_rate] if options[:points_rate]
params[:force_complete] = options[:force_complete] if options[:force_complete]
params[:order_num] = options[:order_id] if options[:order_id]
params[:l_date] = options[:date].to_i if options[:date]
params[:fields] = [:public_key, options[:order_id] && :order_num].compact.join(',')
response = request(:get, '/v1/purchases/new', params)
if response.success?
Purchase.parse(response.data)
else
raise APIError, "Cannot create a purchase: #{response.error_message}"
end
end
# @param [Integer] order_id
# @param [Hash] options
#
# @option options [BigDecimal] :price
def confirm_purchase(order_id, options = {})
params = {:order_num => order_id}
params[:new_price] = options[:price] if options[:price]
response = request(:get, '/v1/purchases/confirm', params)
if response.success?
Purchase.parse(response.data)
else
raise APIError, "Cannot confirm a purchase: #{response.error_message}"
end
end
# @param [String] gift_public_key
def confirm_gift(gift_public_key)
params = {:gift_public_key => gift_public_key}
response = request(:get, '/v1/ecommerce/gifts/commit-transaction', params)
if response.success?
else
raise APIError, "Cannot confirm a gift: #{response.error_message}"
end
end
# @param [String] user_id origin user id
# @param [Integer] points amount of points to deposit to the user's account
# @param [String] comment
def add_points(user_id, points, comment = nil)
params = {:origin_user_id => user_id, :points => points, :comment => comment}
response = request(:get, '/v2/points/add', params)
if response.success?
response.data[:public_key]
else
raise APIError, "Cannot add points: #{response.error_message}"
end
end
def request(method, url, params = {})
execute_request(method, url, auth_params.merge(params))
end
def logger
Sailplay.logger
end
private
def auth_params
{:store_department_id => store_id, :token => api_token}
end
def api_url(url = '')
URI.parse("#{protocol}://#{host}:#{port}").merge("#{endpoint}/#{url}").to_s
end
def api_token
@api_token ||= login
end
def login
raise ConfigurationError, 'Missing client configuration: ' +
'please check that store_id, store_key and pin_code are ' +
'configured' unless credentials?
response = execute_request(:get, '/v1/login', credentials)
if response.success?
response.data[:token]
else
raise AuthenticationError.new("Cannot authenticate on Sailplay. Check your config. (Response: #{response})")
end
end
def credentials
{
:store_department_id => @store_id,
:store_department_key => @store_key,
:pin_code => @store_pin
}
end
def credentials?
credentials.values.all?
end
def execute_request(method, url, params = {})
logger.debug(self.class) {"Starting #{method} request to #{url} with #{params.inspect}"}
url = api_url(url)
headers = @connection_options[:headers].merge(:params => params)
request_opts = {:method => method, :url => url, :headers => headers}
begin
response = RestClient::Request.execute(request_opts)
rescue RestClient::ExceptionWithResponse => e
if e.http_code && e.http_body
handle_api_error(e.http_code, e.http_body)
else
handle_restclient_error(e)
end
rescue RestClient::Exception, SocketError, Errno::ECONNREFUSED => e
handle_restclient_error(e)
end
http_code, http_body = response.code, response.body
logger.debug(self.class) {"\t HTTP Code -> #{http_code}"}
#logger.debug(self.class) {"\t HTTP Body -> #{http_body}"}
begin
json_body = MultiJson.load(http_body, :symbolize_keys => true)
rescue MultiJson::DecodeError
raise APIError.new("Invalid response object from API: #{http_body.inspect} (HTTP response code was #{http_code})", http_code, http_body)
end
#logger.debug(self.class) {"\t JSON Body -> #{json_body}"}
response = Response.new(json_body)
logger.debug(self.class) {"\t Valid -> #{response.success?}"}
logger.debug(self.class) {"\t Payload -> #{response.data}"}
response
end
def handle_restclient_error(e)
case e
when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
message = "Could not connect to Sailplay (#{endpoint}). " +
"Please check your internet connection and try again. " +
"If this problem persists, let us know at support@sailplay.ru."
when SocketError
message = "Unexpected error communicating when trying to connect to Sailplay. " +
"HINT: You may be seeing this message because your DNS is not working. " +
"To check, try running 'host sailplay.ru' from the command line."
else
message = "Unexpected error communicating with Sailplay. " +
"If this problem persists, let us know at support@sailplay.ru."
end
message += "\n\n(Network error: #{e.message})"
raise APIConnectionError, message
end
def handle_api_error(http_code, http_body)
begin
error_obj = MultiJson.load(http_body, :symbolize_keys => true)
message = error_obj[:message]
rescue MultiJson::DecodeError
message = "Invalid response object from API: #{http_body.inspect} (HTTP response code was #{http_code})"
raise APIError.new(message, http_code, http_body)
end
case http_code
when 400, 404 then
raise InvalidRequestError.new(message, http_code, http_body, error_obj)
when 401
raise AuthenticationError.new(message, http_code, http_body, error_obj)
else
raise APIError.new(message, http_code, http_body, error_obj)
end
end
end
end