webpay 2.0.0

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.
data/lib/webpay.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'webpay/version'
2
+
3
+ # Toplevel module of WebPay gem.
4
+ # This is the start point.
5
+ module WebPay
6
+ autoload(:Client, 'webpay/client')
7
+ autoload(:Operations, 'webpay/operations')
8
+ autoload(:WebPayError, 'webpay/webpay_error')
9
+ autoload(:APIConnectionError, 'webpay/webpay_error')
10
+ autoload(:APIError, 'webpay/webpay_error')
11
+ autoload(:AuthenticationError, 'webpay/webpay_error')
12
+ autoload(:CardError, 'webpay/webpay_error')
13
+ autoload(:InvalidRequestError, 'webpay/webpay_error')
14
+ autoload(:Entity, 'webpay/entity')
15
+ autoload(:EntityList, 'webpay/entity_list')
16
+ autoload(:Account, 'webpay/account')
17
+ autoload(:Card, 'webpay/card')
18
+ autoload(:Charge, 'webpay/charge')
19
+ autoload(:Customer, 'webpay/customer')
20
+ autoload(:Event, 'webpay/event')
21
+ autoload(:Token, 'webpay/token')
22
+ autoload(:ResponseConverter, 'webpay/response_converter')
23
+
24
+ @api_base = 'https://api.webpay.jp'
25
+ @api_version = '/v1'
26
+ @api_key = nil
27
+
28
+ class << self
29
+ # Absolute path to SSL CA file.
30
+ # This gem includes SSL CA file as lib/data/ca-certificates.crt.
31
+ def ssl_ca_file
32
+ File.join(File.dirname(File.expand_path(__FILE__)), 'data', 'ca-certificates.crt')
33
+ end
34
+
35
+ # Current client object.
36
+ # client is memoized, and nullified when @api_base or @api_key is modified.
37
+ def client
38
+ @client ||= Client.new(@api_key, @api_base, @api_version)
39
+ end
40
+
41
+ # Set api_base, the base URL of API.
42
+ # Configure this before sending any request.
43
+ # Take care when using unofficial WebPay API.
44
+ def api_base=(new_value)
45
+ @api_base = new_value
46
+ @client = nil
47
+ end
48
+
49
+ # Set api_key, your token for accessing API.
50
+ # Configure this before sending any request.
51
+ def api_key=(new_value)
52
+ @api_key = new_value
53
+ @client = nil
54
+ end
55
+
56
+ # Get current api_base
57
+ def api_base
58
+ @api_base
59
+ end
60
+
61
+ # Get current api_key
62
+ def api_key
63
+ @api_key
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,20 @@
1
+ module WebPay
2
+
3
+ # Object for API response hash object with <code>hash['object'] = account</code>
4
+ class Account < Entity
5
+ class << self
6
+
7
+ # Get the account of api_key's owner
8
+ # @return [Account] the account of api_key's owner
9
+ def retrieve
10
+ convert(WebPay.client.get(path))
11
+ end
12
+
13
+ # @api private
14
+ # @return [String] Relative path to API root: <code>'/account'</code>
15
+ def path
16
+ '/account'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ module WebPay
2
+
3
+ # Object for API response hash object with <code>hash['object'] = card</code>.
4
+ # Card is not accessible as an API endpoint
5
+ class Card < Entity
6
+
7
+ # Check equality with rhs
8
+ # @return [Boolean] true if rhs is the same object, or it is a Card with the same fingerprint value
9
+ def ==(other)
10
+ other.equals?(self) || (other.instance_of?(self.class) && other.fingerprint == self.fingerprint)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ module WebPay
2
+
3
+ # Object for API response hash object with <code>hash['object'] = charge</code>
4
+ class Charge < Entity
5
+ install_class_operations :create, :retrieve, :all
6
+
7
+ # @return [String] Relative path to API root
8
+ # @api private
9
+ def self.path
10
+ '/charges'
11
+ end
12
+
13
+ # Refund this charge
14
+ # @param [Hash] params request parameters
15
+ # @option params [Integer] :amount The amount to refund. Default is all amount.
16
+ def refund(params = {})
17
+ update_attributes(WebPay.client.post([path, 'refund'].join('/'), params))
18
+ end
19
+
20
+ # Capture a not captured charge
21
+ # @param [Hash] params request parameters
22
+ # @option params [Integer] :amount The amount to capture. Default is all amount.
23
+ def capture(params = {})
24
+ update_attributes(WebPay.client.post([path, 'capture'].join('/'), params))
25
+ end
26
+
27
+ # @return [String] Relative path of instance to API root
28
+ # @api private
29
+ def path
30
+ "/charges/#{id}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module WebPay
5
+
6
+ # API client wrapping of Faraday.
7
+ # Not intended to use directly.
8
+ class Client
9
+
10
+ # Initialize client
11
+ #
12
+ # @param api_key [String] User's secret API key
13
+ # @param api_base [String] the URL without trailing '/'.
14
+ # @param api_version [String] the path indicating API version. `/v1`.
15
+ #
16
+ # @example Client for the default endpoint
17
+ # Client.new('test_secret_XXXX', 'https://api.webpay.jp', '/v1')
18
+ def initialize(api_key, api_base, api_version)
19
+ ssl_options = {ca_file: WebPay.ssl_ca_file}
20
+ default_headers = {
21
+ 'Authorization' => "Bearer #{api_key}",
22
+ 'User-Agent' => "WebPay#{api_version} RubyBinding/#{WebPay::VERSION}"
23
+ }
24
+ @conn = Faraday.new(api_base, ssl: ssl_options, headers: default_headers) do |builder|
25
+ builder.request :url_encoded
26
+ builder.adapter Faraday.default_adapter
27
+ end
28
+ @api_version = api_version
29
+ end
30
+
31
+ # Convert faraday response to a hash by decoding JSON.
32
+ # This raises error if the response indicates error status.
33
+ #
34
+ # @api private
35
+ # @param response [Faraday::Response]
36
+ # @return [Hash] Raw object
37
+ # @raise [WebPay::WebPayError] For invalid requests (4xx) or internal server error (5xx)
38
+ def handle_response(response)
39
+ case response.status
40
+ when 200..299
41
+ begin
42
+ JSON.parse(response.body)
43
+ rescue JSON::ParserError => e
44
+ raise WebPay::APIConnectionError.new("Response JSON is broken. #{e.message}: #{response.body}", e)
45
+ end
46
+ else
47
+ raise WebPay::WebPayError.from_response(response.status, response.body)
48
+ end
49
+ end
50
+
51
+ Faraday::Connection::METHODS.each do |method|
52
+ define_method(method) do |url, *args|
53
+ begin
54
+ response = @conn.__send__(method, @api_version + url, *args)
55
+ rescue Faraday::Error::ClientError => e
56
+ raise WebPay::APIConnectionError.faraday_error(e)
57
+ end
58
+ handle_response(response)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,70 @@
1
+ module WebPay
2
+
3
+ # Object for API response hash object with <code>hash['object'] = customer</code>
4
+ class Customer < Entity
5
+ install_class_operations :create, :retrieve, :all
6
+
7
+ # Attributes updated by assignment.
8
+ # These attributes are sent on save.
9
+ # Only description, card, and email are effective.
10
+ attr_accessor :updated_attributes
11
+
12
+ # @return [String] Relative path to API root
13
+ # @api private
14
+ def self.path
15
+ '/customers'
16
+ end
17
+
18
+ # @api private
19
+ def initialize(attributes)
20
+ @updated_attributes = {}
21
+ super(attributes)
22
+ end
23
+
24
+ # <code>object['key']=</code> is wrapper for <code>object.key =</code>.
25
+ # Method call style is recommended.
26
+ # @return [Object] Given value
27
+ def []=(key, value)
28
+ send("#{key}=", value)
29
+ end
30
+
31
+ # <code>object['key']</code> is wrapper for <code>object.key</code>.
32
+ # Method call style is recommended.
33
+ # @return [Object] The attribute's value
34
+ def [](key)
35
+ send(key)
36
+ end
37
+
38
+ [:description, :card, :email].each do |attr|
39
+ define_method("#{attr}=") do |value|
40
+ @updated_attributes[attr.to_s] = value
41
+ end
42
+ define_method("#{attr}") do
43
+ @updated_attributes[attr.to_s] || @attributes[attr.to_s]
44
+ end
45
+ end
46
+
47
+ # Send update request of modified attributes.
48
+ # description, card, and email are modifiable.
49
+ # @return [Customer] this object with attributes updated
50
+ def save
51
+ update_attributes(WebPay.client.post(path, @updated_attributes))
52
+ @updated_attributes = {}
53
+ self
54
+ end
55
+
56
+ # Delete this customer.
57
+ # This operation cannot be undone.
58
+ # @return [Boolean] true if operation succeeded
59
+ def delete
60
+ response = WebPay.client.delete(path)
61
+ response['deleted']
62
+ end
63
+
64
+ # @return [String] Relative path of instance to API root
65
+ # @api private
66
+ def path
67
+ "/customers/#{id}"
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,63 @@
1
+ module WebPay
2
+
3
+ # The base class for entity objects
4
+ # @abstract
5
+ class Entity
6
+ class << self
7
+ include Operations
8
+
9
+ # Convert hash API response into an entity object
10
+ # @param [Hash] hash Raw API response
11
+ # @return [Entity] An entity object corresponds to hash
12
+ def convert(hash)
13
+ converter = ResponseConverter.new
14
+ converter.convert(hash)
15
+ end
16
+ end
17
+
18
+ # Attributes of this object.
19
+ # <code>method_missing</code> and <code>#[]</code> are provided to access attributes
20
+ attr_reader :attributes
21
+
22
+ # Initialized by ResponseConverter
23
+ # @api private
24
+ def initialize(attributes)
25
+ @attributes = attributes
26
+ end
27
+
28
+ # @return [Boolean] true if rhs is the same object or has the same id
29
+ def ==(other)
30
+ other.equal?(self) || (other.instance_of?(self.class) && other.id == self.id)
31
+ end
32
+
33
+ # Access attribute.
34
+ # Using the attribute name as a method is recommended.
35
+ # @return [Object] The attribute's value
36
+ def [](key)
37
+ send(key)
38
+ end
39
+
40
+ # Provide access to attributes
41
+ # @return [Object] The attribute's value
42
+ def method_missing(method_name, *args, &block)
43
+ key = method_name.to_s
44
+ if @attributes.has_key?(key)
45
+ @attributes[key]
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def respond_to_missing?(method_name, include_private = false)
52
+ @attributes.has_key?(method_name.to_s) || super
53
+ end
54
+
55
+ private
56
+ def update_attributes(attributes)
57
+ raise "unexpected object" if attributes['object'] != @attributes['object']
58
+ new_object = ResponseConverter.new.convert(attributes)
59
+ @attributes = new_object.attributes
60
+ self
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ module WebPay
2
+
3
+ # Object for API response hash object with <code>hash['object'] = list</code>.
4
+ # This enumerates containing data.
5
+ class EntityList < Entity
6
+ include Enumerable
7
+
8
+ # @return [Boolean] true if rhs is the same object or id lists of containing data are the same
9
+ def ==(other)
10
+ other.equal?(self) ||
11
+ (other.instance_of?(self.class) && other.data.map(&:id) == self.data.map(&:id))
12
+ end
13
+
14
+ # Return the all entry count, not limited by <code>limit</code>.
15
+ # This overrides <code>Enumerable#count</code>. Not necessarily equal to the data size.
16
+ def count
17
+ @attributes['count']
18
+ end
19
+
20
+ # Enumerate containing data.
21
+ def each(&block)
22
+ self.data.each(&block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module WebPay
2
+
3
+ # Object for API response hash object with <code>hash['object'] = event</code>
4
+ class Event < Entity
5
+ install_class_operations :retrieve, :all
6
+
7
+ # @return [String] Relative path to API root
8
+ # @api private
9
+ def self.path
10
+ '/events'
11
+ end
12
+
13
+ # Entity object for <code>event.data</code>
14
+ # This entity has <code>object</code> on which event occurred,
15
+ # and <code>previous_attributes</code> if some of attributes are modified on the event.
16
+ class Data < Entity
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ module WebPay
2
+
3
+ # This module installs collection operations to entities.
4
+ # Some of <code>create</code>, <code>retrieve</code>, and <code>all</code> are defined as class methods.
5
+ module Operations
6
+
7
+ # Install selected operations
8
+ # @api private
9
+ def install_class_operations(*operations)
10
+ define_create if operations.include?(:create)
11
+ define_retrieve if operations.include?(:retrieve)
12
+ define_all if operations.include?(:all)
13
+ end
14
+
15
+ # Define <code>create</code> method
16
+ # @api private
17
+ def define_create
18
+ instance_eval do
19
+ def create(params = {})
20
+ convert(WebPay.client.post(path, params))
21
+ end
22
+ end
23
+ end
24
+
25
+ # Define <code>retrieve(id)</code> method
26
+ # @api private
27
+ def define_retrieve
28
+ instance_eval do
29
+ def retrieve(id)
30
+ id = id.to_s
31
+ if id.strip == ''
32
+ raise InvalidRequestError.invalid_id(id)
33
+ end
34
+ convert(WebPay.client.get([path, id].join('/')))
35
+ end
36
+ end
37
+ end
38
+
39
+ # Define <code>all</code> method
40
+ # @api private
41
+ def define_all
42
+ instance_eval do
43
+ def all(params = {})
44
+ convert(WebPay.client.get(path, params))
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,45 @@
1
+ module WebPay
2
+
3
+ # Converts raw Hash response to an entity
4
+ class ResponseConverter
5
+
6
+ # Converts raw Hash response to an entity.
7
+ # This recursively changes internal objects.
8
+ # @param [Hash] attributes Raw API response
9
+ # @return [Entity] Corresponding entity
10
+ def convert(attributes)
11
+ case attributes['object']
12
+ when 'card'
13
+ Card.new(attributes)
14
+ when 'charge'
15
+ if attributes['card']
16
+ attributes['card'] = convert(attributes['card'])
17
+ end
18
+ Charge.new(attributes)
19
+ when 'customer'
20
+ if attributes['active_card']
21
+ attributes['active_card'] = convert(attributes['active_card'])
22
+ end
23
+ Customer.new(attributes)
24
+ when 'token'
25
+ if attributes['card']
26
+ attributes['card'] = convert(attributes['card'])
27
+ end
28
+ Token.new(attributes)
29
+ when 'event'
30
+ if attributes['data']
31
+ attributes['data']['object'] = convert(attributes['data']['object'])
32
+ attributes['data'] = Event::Data.new(attributes['data'])
33
+ end
34
+ Event.new(attributes)
35
+ when 'account'
36
+ Account.new(attributes)
37
+
38
+ when 'list'
39
+ attributes['data'] ||= []
40
+ attributes['data'].map! { |line| convert(line) }
41
+ EntityList.new(attributes)
42
+ end
43
+ end
44
+ end
45
+ end