# frozen_string_literal: true module Stripe class Account < APIResource extend Gem::Deprecate extend Stripe::APIOperations::Create include Stripe::APIOperations::Delete extend Stripe::APIOperations::List include Stripe::APIOperations::Save extend Stripe::APIOperations::NestedResource OBJECT_NAME = "account".freeze custom_method :reject, http_verb: :post nested_resource_class_methods :capability, operations: %i[retrieve update list], resource_plural: "capabilities" nested_resource_class_methods :person, operations: %i[create retrieve update delete list] def reject(params = {}, opts = {}) resp, opts = request(:post, resource_url + "/reject", params, opts) Util.convert_to_stripe_object(resp.data, opts) end save_nested_resource :external_account nested_resource_class_methods :external_account, operations: %i[create retrieve update delete list] nested_resource_class_methods :login_link, operations: %i[create] # This method is deprecated. Please use `#external_account=` instead. save_nested_resource :bank_account deprecate :bank_account=, "#external_account=", 2017, 8 def resource_url if self["id"] super else "/v1/account" end end # @override To make id optional def self.retrieve(id = ARGUMENT_NOT_PROVIDED, opts = {}) id = if id.equal?(ARGUMENT_NOT_PROVIDED) nil else Util.check_string_argument!(id) end # Account used to be a singleton, where this method's signature was # `(opts={})`. For the sake of not breaking folks who pass in an OAuth # key in opts, let's lurkily string match for it. if opts == {} && id.is_a?(String) && id.start_with?("sk_") # `super` properly assumes a String opts is the apiKey and normalizes # as expected. opts = id id = nil end super(id, opts) end def persons(params = {}, opts = {}) resp, opts = request(:get, resource_url + "/persons", params, opts) Util.convert_to_stripe_object(resp.data, opts) end # We are not adding a helper for capabilities here as the Account object # already has a capabilities property which is a hash and not the sub-list # of capabilities. # Somewhat unfortunately, we attempt to do a special encoding trick when # serializing `additional_owners` under an account: when updating a value, # we actually send the update parameters up as an integer-indexed hash # rather than an array. So instead of this: # # field[]=item1&field[]=item2&field[]=item3 # # We send this: # # field[0]=item1&field[1]=item2&field[2]=item3 # # There are two major problems with this technique: # # * Entities are addressed by array index, which is not stable and can # easily result in unexpected results between two different requests. # # * A replacement of the array's contents is ambiguous with setting a # subset of the array. Because of this, the only way to shorten an # array is to unset it completely by making sure it goes into the # server as an empty string, then setting its contents again. # # We're trying to get this overturned on the server side, but for now, # patch in a special allowance. def serialize_params(options = {}) serialize_params_account(self, super, options) end def serialize_params_account(_obj, update_hash, options = {}) if (entity = @values[:legal_entity]) if (owners = entity[:additional_owners]) entity_update = update_hash[:legal_entity] ||= {} entity_update[:additional_owners] = serialize_additional_owners(entity, owners) end end if (individual = @values[:individual]) if individual.is_a?(Person) && !update_hash.key?(:individual) update_hash[:individual] = individual.serialize_params(options) end end update_hash end def self.protected_fields [:legal_entity] end def legal_entity self["legal_entity"] end def legal_entity=(_legal_entity) raise NoMethodError, "Overriding legal_entity can cause serious issues. Instead, set " \ "the individual fields of legal_entity like " \ "`account.legal_entity.first_name = 'Blah'`" end def deauthorize(client_id = nil, opts = {}) params = { client_id: client_id, stripe_user_id: id, } OAuth.deauthorize(params, opts) end ARGUMENT_NOT_PROVIDED = Object.new private def serialize_additional_owners(legal_entity, additional_owners) original_value = legal_entity .instance_variable_get(:@original_values)[:additional_owners] if original_value && original_value.length > additional_owners.length # url params provide no mechanism for deleting an item in an array, # just overwriting the whole array or adding new items. So let's not # allow deleting without a full overwrite until we have a solution. raise ArgumentError, "You cannot delete an item from an array, you must instead " \ "set a new array" end update_hash = {} additional_owners.each_with_index do |v, i| # We will almost always see a StripeObject except in the case of a Hash # that's been appended to an array of `additional_owners`. We may be # able to normalize that ugliness by using an array proxy object with # StripeObjects that can detect appends and replace a hash with a # StripeObject. update = v.is_a?(StripeObject) ? v.serialize_params : v next unless update != {} && (!original_value || update != legal_entity.serialize_params_value(original_value[i], nil, false, true)) update_hash[i.to_s] = update end update_hash end end end