lib/phaxio/client.rb in phaxio-0.4.1 vs lib/phaxio/client.rb in phaxio-0.5.0

- old
+ new

@@ -1,31 +1,27 @@ module Phaxio include HTTMultiParty base_uri 'https://api.phaxio.com/v1' module Config - attr_accessor :api_key, :api_secret + attr_accessor :api_key, :api_secret, :callback_token end module Client + DIGEST = OpenSSL::Digest.new('sha1') + # Public: Send a fax. # # options - The Hash options used to refine the selection (default: {}): # :to - The Phone Number (i.e. [country # code][number] or just a 10 digit # number in the US or Canada). Put # square brackets after parameter # name to send to multiple # recipients (e.g. to[]) (required). - # :filename - The Binary Stream name of the file - # you want to fax. This is optional - # if you specify string_data. Must - # have file name in the filename - # field of the body-part header. Put - # square brackets after parameter - # name to send multiple files (e.g. - # filename[]) (required). + # :filename - A Ruby File of a document to fax + # (supported file types: https://www.phaxio.com/faq#faq10) # :string_data - A String of html, plain text, or a # URL. If additional files are # specified as well, this data will # be included first in the fax # (optional). @@ -58,18 +54,33 @@ # completed. Must be between 1 and 60 # (optional). # # Examples # - # Phaxio.send_fax(to: "0123456789", filename: "test.pdf") + # Phaxio.send_fax(to: "0123456789", filename: File.new("docToSend.pdf")) # # Returns a HTTParty::Response object containing a success bool, # a String message, and an in faxID. def send_fax(options) send_post("/send", options) end + # Public: Resend a fax. + # + # options - The Hash options used to refine the selection (default: {}): + # :id - The int id of the fax you want to resend (required). + # + # Examples + # + # Phaxio.resend_fax(id: "123456") + # + # Returns a HTTParty::Response object containing a success bool, + # a message string, and data containing the fax ID int. + def resend_fax(options) + send_post("/resendFax", options) + end + # Public: Test receiving a fax. # # options - The Hash options used to refine the selection (default: {}): # from_number - The Phone Number of the simulated sender # (optional). @@ -129,11 +140,11 @@ # Public: Get a detailed list of the phone numbers you current own on # Phaxio. # # options - The Hash options used to refne th selection (default: {}): - # area_code - An integer area code you'd like to filter by + # area_code - An integer area code you'd like to filter by # (optional). # number - A String phone number you'd like to retrieve # (optional). # # Examples @@ -221,10 +232,28 @@ # and a String message. def cancel_fax(options) send_post("/faxCancel", options) end + # Public: Delete a specific fax. + # + # options - The hash options used to refine the selection (defaults: {}): + # :id - The int ID of the fax you want to cancel + # (required). + # :files_only - The bool used to determine whether only the files + # are deleted. If not specified, default is false + # (optional). + # + # Examples + # + # Phaxio.delete_fax(id: 1234, files_only: true) + # + # Returns a HTTParty::Response object with success bool and message string. + def delete_fax(options) + send_post("/deleteFax", options) + end + # Public: Get the status of Client's account. # # Examples # # Phaxio.get_account_status @@ -233,21 +262,235 @@ # (containing faxes_sent_this_month, faxes_sent_today, and balance). def get_account_status send_post("/accountStatus", {}) end + # Public: Attach a PhaxCode to a PDF you provide. + # + # options - Type: hash. Options used to refine the action (default: {}): + # x - Type: float. The x-coordinate (in PDF points*) + # where the PhaxCode should be drawn. x=0 is at the + # left-most point on the page. (required) + # y - Type: float. The y-coordinate (in PDF points*) + # where the PhaxCode should be drawn. Y=0 is the + # bottom-most point on the page. (required) + # filename - A Ruby File in PDF format (required) + # metadata - Type: string. Custom metadata to be associated + # with the created barcode. If not present, the + # basic PhaxCode for your account will be used. + # page_number - Type: integer. The page where the PhaxCode should + # be drawn. 1-based. + # *PDF points definition: A "point" is 1/72 of an inch. An + # 8.5"x11" document is therefore 612 pt x 792 pt. + # + # Examples + # + # Phaxio.attach_phaxcode_to_pdf( + # x: "0", y: "100", filename: File.new("input.pdf") + # ) + # + # Response: A PDF file containing a PhaxCode at the location specified. + def attach_phaxcode_to_pdf(options) + if options[:filename].nil? + raise StandardError, 'You must include a PDF file.' + end + + if options[:x] < 0 || options[:y] < 0 + raise StandardError, 'Coordinates must be greater than or equal to 0.' + end + + send_post('/attachPhaxCodeToPdf', options) + end + + # Public: Create a custom PhaxCode. + # + # options - Type: hash. Options used to refine the action (default: {}): + # metadata - Type: string. Custom metadata to be associated with + # this barcode. If not present, the basic PhaxCode for + # your account will be used. (optional) + # redirect - Type: boolean. If present and true, the PhaxCode + # barcode image will be dumped in the response. + # (optional) + # + # Example: + # Phaxio.create_phaxcode(metadata: "sale_id=44") + # + # Response: If the redirect parameter is not provided, a JSON object with + # success, message, and data attributes is returned. The data + # attribute contains a url where the PhaxCode barcode image can be + # accessed. Otherwise, the image data is dumped in the response. + def create_phaxcode(options = {}) + send_post('/createPhaxCode', options) + end + + # Public: Get a Hosted Document with PhaxCode included + # + # Note: You will have to set up the hosted document with Phaxio (along with + # the relevant PhaxCode) before calling this method. + # + # options - Type: hash. Options used to refine the action (default: {}): + # name - Type: string. The name of a hosted document. + # (required) + # metadata - Type: string. Custom metadata to be associated with + # the PhaxCode that will be attached to the hosted + # document. If not present, the basic PhaxCode for your + # account will be used. + # (optional) + # + # Example: + # Phaxio.get_hosted_document(name:"business_fax") + # + # Response: A PDF copy of the hosted document with a PhaxCode included at + # the pre-specified location. + def get_hosted_document(options) + if options[:name].nil? + raise StandardError, 'You must include the name of the hosted document.' + end + + send_post('/getHostedDocument', options) + end + + # Public: Get a list of supported countries for sending faxes + # + # Note: This method doesn't require API keys and is included for the sake of + # completion. + # + # Example: + # Phaxio.supported_countries + # + # Response: A JSON object with success, message, and data attributes. The + # data attribute contains a hash, where the key contains the name + # of the country, and the value is a hash of attributes for the + # country (currently only pricing information). + # + # Example Response: + # { + # "success": true, + # "message": "Data contains supported countries.", + # "data": { + # "United States": { + # "price_per_page": 7 + # }, + # "Canada": { + # "price_per_page": 7 + # }, + # "United Kingdom": { + # "price_per_page": 10 + # }, + # ... + # } + # } + def supported_countries + post('/supportedCountries') + end + + # Public: List area codes available for purchasing numbers + # + # Note: This method doesn't require API keys and is included for the sake of + # completion. + # + # options - Type: hash. Options used to refine the query (default: {}): + # is_toll_free - Type: boolean. Will only return toll free area + # codes. (optional) + # state - Type: string. A two character state or province + # abbreviation (e.g. IL or YT). Will only return + # area codes for this state. (optional) + # + # Response: A JSON object with success, message, and data attributes. The + # data attribute contains a map of area codes to city and state. + # + # Example response: + # { + # "success": true, + # "message": "295 area codes available.", + # "data": { + # "201": { + # "city": "Bayonne, Jersey City, Union City", + # "state": "New Jersey" + # }, + # "202": { + # "city": "Washington", + # "state": "District Of Columbia" + # }, + # ... a lot more area codes here... + # } + # } + def area_codes(options = {}) + post('/areaCodes', options) + end + def send_post(path, options) - post(path, query: options.merge!({api_key: api_key, api_secret: api_secret})) + post( + path, query: options.merge!(api_key: api_key, api_secret: api_secret) + ) end + + # Public: Check the signature of the signed request. + # + # signature - Type: string. The X-Phaxio-Signature HTTP header value. + # url - Type: string. The full URL that was called by Phaxio, + # including the query. (required) + # params - Type: hash. The POSTed form data (required) + # files - Type: array. Submitted files (required - "received" fax + # callback only) + # + # Returns true if the signature matches the signed request, otherwise false + def valid_callback_signature?(signature, url, params, files = []) + check_signature = generate_check_signature(url, params, files) + check_signature == signature + end + + # Public: Generate a signature using the request data and callback token + # + # url - Type: string. The full URL that was called by Phaxio, + # including the query. (required) + # params - Type: hash. The POSTed form data (required) + # files - Type: array. Submitted files (required - "received" fax + # callback only) + # + # Retuns a signature based on the request data and configured callback + # token, which can then be compared with the request signature. + def generate_check_signature(url, params, files = []) + params_string = generate_params_string(params) + file_string = generate_files_string(files) + callback_data = "#{url}#{params_string}#{file_string}" + OpenSSL::HMAC.hexdigest(DIGEST, callback_token, callback_data) + end + + private + + def generate_params_string(params) + sorted_params = params.sort_by { |key, _value| key } + params_strings = sorted_params.map { |key, value| "#{key}#{value}" } + params_strings.join + end + + def generate_files_string(files) + files_array = files_to_array(files).reject(&:nil?) + sorted_files = files_array.sort_by { |file| file[:name] } + files_strings = sorted_files.map { |file| generate_file_string(file) } + files_strings.join + end + + def files_to_array(files) + files.is_a?(Array) ? files : [files] + end + + def generate_file_string(file) + file[:name] + DIGEST.hexdigest(file[:tempfile].read) + end end - # Public: Configure Phaxio with your api_key and api_secret + # Public: Configure Phaxio with your api_key, api_secret, and the callback + # token provided in your Phaxio account (to verify that requests are + # coming from Phaxio). # # Examples # # Phaxio.config do |config| - # config.api_key = "12345678910" - # config.api_secret = "10987654321" + # config.api_key = '12345678910' + # config.api_secret = '10987654321' + # config.callback_token = '32935829' # end # # Returns nothing. def self.config yield(self)