require "yandex_money/api/version" require "yandex_money/exceptions" require "httparty" require "uri" require "ostruct" module YandexMoney class Api include HTTParty base_uri "https://sp-money.yandex.ru" default_timeout 30 attr_accessor :client_url, :code, :token, :instance_id # Returns url to get token def initialize(options) # TOKEN provided if options.length == 1 && options[:token] != nil @token = options[:token] else @client_id = options[:client_id] @redirect_uri = options[:redirect_uri] @instance_id = options[:instance_id] if options[:scope] != nil @client_url = send_authorize_request( client_id: @client_id, response_type: "code", redirect_uri: @redirect_uri, scope: options[:scope] ) end end end # obtains and saves token from code def obtain_token(client_secret = nil) raise YandexMoney::FieldNotSetError.new(:code) if @code == nil uri = "/oauth/token" options = { code: @code, client_id: @client_id, grant_type: "authorization_code", redirect_uri: @redirect_url } options[:client_secret] = client_secret if client_secret @token = self.class.post(uri, body: options) .parsed_response["access_token"] end # obtains account info def account_info check_token OpenStruct.new send_request("/api/account-info").parsed_response end # obtains operation history def operation_history(options=nil) check_token OpenStruct.new send_request("/api/operation-history", options).parsed_response end # obtains operation details def operation_details(operation_id) check_token request = send_request("/api/operation-details", operation_id: operation_id) details = OpenStruct.new request.parsed_response if details.error raise YandexMoney::ApiError.new details.error else details end end # basic request payment method def request_payment(options) check_token response = with_http_retries do request = send_request("/api/request-payment", options) OpenStruct.new request.parsed_response end if response.error raise YandexMoney::ApiError.new response.error else response end end # basic process payment method def process_payment(options) check_token request = send_request("/api/process-payment", options) response = OpenStruct.new request.parsed_response if response.error raise YandexMoney::ApiError.new response.error else response end end def get_instance_id request = send_request("/api/instance-id", client_id: @client_id) if request["status"] == "refused" raise YandexMoney::ApiError.new request["error"] else request["instance_id"] end end def incoming_transfer_accept(operation_id, protection_code = nil) uri = "/api/incoming-transfer-accept" if protection_code request_body = { operation_id: operation_id, protection_code: protection_code } else request_body = { operation_id: operation_id } end request = send_request("/api/incoming-transfer-accept", request_body) if request["status"] == "refused" raise YandexMoney::AcceptTransferError.new request["error"], request["protection_code_attempts_available"] else true end end def incoming_transfer_reject(operation_id) request = send_request("/api/incoming-transfer-reject", operation_id: operation_id) if request["status"] == "refused" raise YandexMoney::ApiError.new request["error"] else true end end def request_external_payment(payment_options) payment_options[:instance_id] ||= @instance_id request = send_request("/api/request-external-payment", payment_options) if request["status"] == "refused" raise YandexMoney::ApiError.new request["error"] else OpenStruct.new request.parsed_response end end def process_external_payment(payment_options) payment_options[:instance_id] ||= @instance_id request = send_request("/api/process-external-payment", payment_options) if request["status"] == "refused" raise YandexMoney::ApiError.new request["error"] elsif request["status"] == "in_progress" raise YandexMoney::ExternalPaymentProgressError.new request["error"], request["next_retry"] else OpenStruct.new request.parsed_response end end private def send_request(uri, options = nil) request = self.class.post(uri, base_uri: "https://money.yandex.ru", headers: { "Authorization" => "Bearer #{@token}", "Content-Type" => "application/x-www-form-urlencoded" }, body: options) case request.response.code when "403" then raise YandexMoney::InsufficientScopeError when "401" then raise YandexMoney::UnauthorizedError.new request["www-authenticate"] else request end end # Retry when errors def with_http_retries(&block) begin yield rescue Errno::ECONNREFUSED, SocketError, Net::ReadTimeout sleep 1 retry end end def check_token raise YandexMoney::FieldNotSetError.new(:token) unless @token end def send_authorize_request(options) uri = "/oauth/authorize" self.class.post(uri, body: options).request.path.to_s end end end