diff --git a/lib/yookassa.rb b/lib/yookassa.rb index b8ef7b4..e3c2416 100644 --- a/lib/yookassa.rb +++ b/lib/yookassa.rb @@ -21,11 +21,17 @@ module Yookassa end def client - raise ConfigError, "Specify `shop_id` and `api_key` settins in a `.configure` block" if @config.nil? + raise ConfigError, "Specify `shop_id` and `api_key` settings in a `.configure` block" if @config.nil? @client ||= Client.new(shop_id: @config.shop_id, api_key: @config.api_key) end + def partner_api + @partner_api ||= PartnerAPI.new + end + def_delegators :client, :payments, :refunds, :receipts + + def_delegators :partner_api, :stores, :webhooks end end diff --git a/lib/yookassa/entity/collection.rb b/lib/yookassa/entity/collection.rb index 499f307..a5d0c9a 100644 --- a/lib/yookassa/entity/collection.rb +++ b/lib/yookassa/entity/collection.rb @@ -4,6 +4,7 @@ require_relative "./types" require_relative "./payment" require_relative "./receipt" require_relative "./refund" +require_relative "./webhook" module Yookassa module Entity @@ -23,5 +24,9 @@ module Yookassa class ReceiptCollection < Collection attribute :items, Types::Array.of(Receipt) end + + class WebhookCollection < Collection + attribute :items, Types::Array.of(Webhook) + end end end diff --git a/lib/yookassa/entity/store_info.rb b/lib/yookassa/entity/store_info.rb new file mode 100644 index 0000000..1638b5d --- /dev/null +++ b/lib/yookassa/entity/store_info.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative "./types" + +module Yookassa + module Entity + class StoreInfo < Dry::Struct + # Store's ID in YooMoney. + attribute :account_id, Types::Coercible::String + + # This is the Demo store + attribute :test, Types::Bool + + # Sending receipts to online sales register enabled in store's settings. + attribute :fiscalization_enabled, Types::Bool + + # List of payment methods available to a store. + attribute :payment_methods, Types::Array.of(Types::String) + + # Store's status. Possible values: enabled + # – store is signed up for YooMoney and it can accept payments; disabled + # – store can't accept payments (not signed up yet, closed, or temporarily unavailable). + attribute :status, Types::String.enum("enabled", "disabled") + + # Store's INN (TIN): 10 or 12 digits. + attribute? :itn, Types::Coercible::String.constrained(max_size: 12, min_size: 10) + end + end +end diff --git a/lib/yookassa/entity/webhook.rb b/lib/yookassa/entity/webhook.rb new file mode 100644 index 0000000..b1620aa --- /dev/null +++ b/lib/yookassa/entity/webhook.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require_relative "./types" + +module Yookassa + module Entity + class Webhook < Dry::Struct + attribute? :idempotency_key, Types::String + + # webhooks's ID in YooMoney + attribute :id, Types::String + + # Event that YooMoney sends notifications for. + attribute :event, Types::String.enum( + "payment.waiting_for_capture", # payment status changed to waiting_for_capture + "payment.succeeded", # payment status changed to succeeded + "payment.canceled", # payment status changed to canceled + "refund.succeeded" # refund status changed to succeeded + ) + + # URL for sending YooMoney's notifications. + attribute :url, Types::String + end + end +end diff --git a/lib/yookassa/partner_api.rb b/lib/yookassa/partner_api.rb new file mode 100644 index 0000000..ed01db1 --- /dev/null +++ b/lib/yookassa/partner_api.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require "http" + +require_relative "./stores" +require_relative "./webhooks" +require_relative "./entity/error" + +module Yookassa + class PartnerAPI + API_URL = "https://api.yookassa.ru/v3/" + + def initialize(oauth_token:) + @http = HTTP.headers("Authorization" => "Bearer #{oauth_token}") + .headers(accept: "application/json") + end + + def stores + @stores ||= Stores.new(self) + end + + def webhooks + @webhooks ||= Webhooks.new(self) + end + + def get(endpoint) + api_call { get("#{API_URL}#{endpoint}", params: query) } + end + + def post(endpoint, idempotency_key:, payload: {}) + api_call { http.headers("Idempotence-Key" => idempotency_key).post("#{API_URL}#{endpoint}", json: payload) } + end + + def delete(endpoint, idempotency_key:) + HTTP.headers("Authorization" => "Bearer #{oauth_token}") + .headers("Idempotence-Key" => idempotency_key) + .delete("#{API_URL}#{endpoint}") + end + + private + + attr_reader :http + + def api_call + response = yield if block_given? + body = JSON.parse(response.body.to_s, symbolize_names: true) + return body if response.status.success? + + Entity::Error.new(**body) + end + end +end diff --git a/lib/yookassa/stores.rb b/lib/yookassa/stores.rb new file mode 100644 index 0000000..883edf0 --- /dev/null +++ b/lib/yookassa/stores.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative "./entity/store_info" + +module Yookassa + class Stores + def initialize(partner_api) + @partner_api = partner_api + end + + def me + data = partner_api.get("me") + Entity::StoreInfo.new(**data) + end + + private + + attr_reader :partner_api + end +end diff --git a/lib/yookassa/webhooks.rb b/lib/yookassa/webhooks.rb new file mode 100644 index 0000000..dea6f8b --- /dev/null +++ b/lib/yookassa/webhooks.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative "./entity/webhook" +require_relative "./entity/collection" + +module Yookassa + class Webhooks + def initialize(partner_api) + @partner_api = partner_api + end + + def create(payload:, idempotency_key: SecureRandom.hex(10)) + data = partner_api.post("webhooks", payload: payload, idempotency_key: idempotency_key) + Entity::Webhook.new(**data.merge(idempotency_key: idempotency_key)) + end + + def list + data = partner_api.get("webhooks") + Entity::WebhookCollection.new(**data) + end + + def delete(webhook_id:) + partner_api.delete("webhooks/#{webhook_id}") + true + end + + private + + attr_reader :partner_api + end +end