diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d3f1a74..01ee55f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,7 @@ jobs: ruby: - 2.6 - 2.7 + - 3.0 steps: - uses: actions/checkout@v2 @@ -24,5 +25,7 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - name: Cops & Specs - run: bin/rake + - name: Cops + run: bundle exec rubocop + - name: Specs + run: bundle exec rspec diff --git a/Gemfile b/Gemfile index ffc917a..a162d37 100644 --- a/Gemfile +++ b/Gemfile @@ -15,4 +15,5 @@ group :development, :test do gem "rubocop", "~> 1.21" gem "rubocop-rake", "~> 0.6.0" gem "rubocop-rspec", "~> 2.5" + gem "webmock", "~> 3.14" end diff --git a/Gemfile.lock b/Gemfile.lock index 8bb2051..4724c10 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,35 +2,42 @@ PATH remote: . specs: yookassa (0.1.0) - evil-client (~> 3.0) + dry-initializer + http (~> 5.0.1) GEM remote: https://rubygems.org/ specs: - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) ast (2.4.2) byebug (11.0.1) coderay (1.1.2) - concurrent-ruby (1.1.7) - crack (0.4.3) - safe_yaml (~> 1.0.0) + crack (0.4.5) + rexml diff-lcs (1.3) docile (1.3.1) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) dry-initializer (3.0.4) - evil-client (3.0.4) - dry-initializer (>= 2.1, < 4) - mime-types (~> 3.1) - rack (~> 2) - tram-policy (>= 0.3.1, < 3) - hashdiff (0.4.0) - i18n (1.8.5) - concurrent-ruby (~> 1.0) + ffi (1.15.4) + ffi-compiler (1.0.1) + ffi (>= 1.0.0) + rake + hashdiff (1.0.1) + http (5.0.1) + addressable (~> 2.3) + http-cookie (~> 1.0) + http-form_data (~> 2.2) + llhttp-ffi (~> 0.3.0) + http-cookie (1.0.4) + domain_name (~> 0.5) + http-form_data (2.3.0) json (2.2.0) + llhttp-ffi (0.3.1) + ffi-compiler (~> 1.0) + rake (~> 13.0) method_source (0.9.2) - mime-types (3.3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2020.1104) parallel (1.21.0) parser (3.0.2.0) ast (~> 2.4.1) @@ -40,8 +47,7 @@ GEM pry-byebug (3.7.0) byebug (~> 11.0) pry (~> 0.10) - public_suffix (3.1.0) - rack (2.2.3) + public_suffix (4.0.6) rainbow (3.0.0) rake (13.0.6) regexp_parser (2.1.1) @@ -75,20 +81,19 @@ GEM rubocop-rspec (2.5.0) rubocop (~> 1.19) ruby-progressbar (1.11.0) - safe_yaml (1.0.5) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - tram-policy (2.0.1) - dry-initializer (> 2, < 4) - i18n (~> 1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8) unicode-display_width (2.1.0) - webmock (3.5.1) - addressable (>= 2.3.6) + webmock (3.14.0) + addressable (>= 2.8.0) crack (>= 0.3.2) - hashdiff + hashdiff (>= 0.4.0, < 2.0.0) PLATFORMS ruby @@ -102,7 +107,7 @@ DEPENDENCIES rubocop-rake (~> 0.6.0) rubocop-rspec (~> 2.5) simplecov (~> 0.16) - webmock (~> 3.5) + webmock (~> 3.14) yookassa! BUNDLED WITH diff --git a/lib/yookassa.rb b/lib/yookassa.rb index f1a429d..387e120 100644 --- a/lib/yookassa.rb +++ b/lib/yookassa.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "evil/client" - +require "http" +require "dry/initializer" require "yookassa/version" require "yookassa/payment" require "yookassa/refund" @@ -12,6 +12,7 @@ require "yookassa/entity/payment" require "yookassa/entity/refund" require "yookassa/error" require "yookassa/config" +require "yookassa/http_helpers" module Yookassa def self.configure diff --git a/lib/yookassa/http_helpers.rb b/lib/yookassa/http_helpers.rb new file mode 100644 index 0000000..9d2c1aa --- /dev/null +++ b/lib/yookassa/http_helpers.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Yookassa + module HttpHelpers + API_URL = "https://api.yookassa.ru/v3/" + + def get(endpoint, query: {}) + response = client.get("#{API_URL}#{endpoint}", params: query) + + return Error.new(response.parse) if response.status.client_error? + + yield(response.parse) if block_given? + end + + def post(endpoint, idempotency_key:, payload: {}) + response = client.headers("Idempotence-Key" => idempotency_key).post("#{API_URL}#{endpoint}", json: payload) + + return Error.new(response.parse) if response.status.client_error? + + yield(response.parse) if block_given? + end + + def client + HTTP.basic_auth(user: shop_id, pass: api_key).headers(accept: "application/json") + end + end +end diff --git a/lib/yookassa/payment.rb b/lib/yookassa/payment.rb index ec769cd..ba07890 100644 --- a/lib/yookassa/payment.rb +++ b/lib/yookassa/payment.rb @@ -1,65 +1,46 @@ # frozen_string_literal: true +require_relative "http_helpers" + module Yookassa - class Payment < Evil::Client - option :shop_id, proc(&:to_s), default: proc { Yookassa.config.shop_id } - option :api_key, proc(&:to_s), default: proc { Yookassa.config.api_key } + class Payment + include HttpHelpers - path { "https://api.yookassa.ru/v3/payments" } - security { basic_auth shop_id, api_key } + attr_reader :shop_id, :api_key - operation :get_payment_info do - option :payment_id, proc(&:to_s) - - http_method :get - path { "/#{payment_id}" } - - response(200) { |*res| Entity::Payment.build(*res) } - response(400, 404) { |*res| Error.build(*res) } + def initialize(shop_id: Yookassa.config.shop_id, api_key: Yookassa.config.api_key) + @shop_id = shop_id + @api_key = api_key end - operation :create do - option :payment - option :idempotency_key, proc(&:to_s) - - http_method :post - - format "json" - headers { { "Idempotence-Key" => idempotency_key } } - body { payment } - - response(200) { |*res| Entity::Payment.build(*res) } - response(400) { |*res| Error.build(*res) } + def get_payment_info(payment_id:) + get("payments/#{payment_id}") do |response| + Entity::Payment.new(response) + end end - operation :capture do - option :payment_id, proc(&:to_s) - option :idempotency_key, optional: true - - http_method :post - - path { "/#{payment_id}/capture" } - - format "json" - headers { { "Idempotence-Key" => idempotency_key } } - - response(200) { |*res| Entity::Payment.build(*res) } - response(400) { |*res| Error.build(*res) } + def create(payment:, idempotency_key: SecureRandom.hex(10)) + post("payments", payload: payment, idempotency_key: idempotency_key) do |response| + Entity::Payment.new(response) + end end - operation :cancel do - option :payment_id, proc(&:to_s) - option :idempotency_key, optional: true - - http_method :post - - path { "/#{payment_id}/cancel" } - - format "json" - headers { { "Idempotence-Key" => idempotency_key } } - - response(200) { |*res| Entity::Payment.build(*res) } - response(400) { |*res| Error.build(*res) } + def capture(payment_id:, idempotency_key: SecureRandom.hex(10)) + post("payments/#{payment_id}/capture", idempotency_key: idempotency_key) do |response| + Entity::Payment.new(response) + end end + + def cancel(payment_id:, idempotency_key: SecureRandom.hex(10)) + post("payments/#{payment_id}/cancel", idempotency_key: idempotency_key) do |response| + Entity::Payment.new(response) + end + end + + # def self.list(shop_id: Yookassa.config.shop_id, api_key: Yookassa.config.api_key) + # get("payments", shop_id: shop_id, api_key: api_key) do |resp| + # resp['items'].map { Entity::Payment.new(_1) } + # end + # end end end diff --git a/lib/yookassa/refund.rb b/lib/yookassa/refund.rb index b02983a..54584ac 100644 --- a/lib/yookassa/refund.rb +++ b/lib/yookassa/refund.rb @@ -1,35 +1,28 @@ # frozen_string_literal: true +require_relative "http_helpers" + module Yookassa - class Refund < Evil::Client - option :shop_id, proc(&:to_s), default: proc { Yookassa.config.shop_id } - option :api_key, proc(&:to_s), default: proc { Yookassa.config.api_key } + class Refund + include HttpHelpers - path { "https://api.yookassa.ru/v3/refunds" } - security { basic_auth shop_id, api_key } + attr_reader :shop_id, :api_key - operation :get_refund_info do - option :payment_id, proc(&:to_s) - - http_method :get - path { "/#{payment_id}" } - - response(200) { |*res| Entity::Refund.build(*res) } - response(400, 404) { |*res| Error.build(*res) } + def initialize(shop_id: Yookassa.config.shop_id, api_key: Yookassa.config.api_key) + @shop_id = shop_id + @api_key = api_key end - operation :create do - option :payload - option :idempotency_key, proc(&:to_s) + def get_refund_info(payment_id:) + get("refunds/#{payment_id}") do |response| + Entity::Refund.new(response) + end + end - http_method :post - - format "json" - headers { { "Idempotence-Key" => idempotency_key } } - body { payload } - - response(200) { |*res| Entity::Refund.build(*res) } - response(400, 404) { |*res| Error.build(*res) } + def create(payload:, idempotency_key: SecureRandom.hex(10)) + post("refunds", payload: payload, idempotency_key: idempotency_key) do |response| + Entity::Refund.new(response) + end end end end diff --git a/spec/fixtures/refund.json b/spec/fixtures/refund.json index 0018bef..d4d4aae 100644 --- a/spec/fixtures/refund.json +++ b/spec/fixtures/refund.json @@ -1,8 +1,8 @@ { "amount": { - "value": "8.00", - "currency": "RUB" -}, -"payment_id": "40484c25-1095-4d36-8b17-92672d1e127d", + "value": "8.00", + "currency": "RUB" + }, + "payment_id": "40484c25-1095-4d36-8b17-92672d1e127d", "description": "test refund, idem-key 40484c25-1095-4d36-8b17-92672d1e127d" } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 56a2849..28fa36f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,9 +13,4 @@ RSpec.configure do |config| config.order = :random config.filter_run focus: true config.run_all_when_everything_filtered = true - - config.around(:each) do |example| - stub_request(:any, //) - example.run - end end diff --git a/spec/yookassa/config_spec.rb b/spec/yookassa/config_spec.rb index 69fc1e5..48d6222 100644 --- a/spec/yookassa/config_spec.rb +++ b/spec/yookassa/config_spec.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true -module Yookassa - RSpec.describe Config do - subject { described_class.new } - it { is_expected.to respond_to(:shop_id) } - it { is_expected.to respond_to(:api_key) } - end +RSpec.describe Yookassa::Config do + subject { described_class.new } + it { is_expected.to respond_to(:shop_id) } + it { is_expected.to respond_to(:api_key) } end diff --git a/spec/yookassa/payment_spec.rb b/spec/yookassa/payment_spec.rb index 3d0f2fa..1ae3228 100644 --- a/spec/yookassa/payment_spec.rb +++ b/spec/yookassa/payment_spec.rb @@ -3,7 +3,10 @@ RSpec.describe Yookassa::Payment do let(:settings) { { shop_id: "SHOP_ID", api_key: "API_KEY" } } let(:idempotency_key) { 12_345 } - let(:payment) { described_class.new(settings) } + let(:payment) { described_class.new(**settings) } + let(:body) { File.read("spec/fixtures/payment_response.json") } + + before { stub_request(:any, //).to_return(body: body, headers: { "Content-Type" => "application/json" }) } shared_examples "returns_payment_object" do it "returns success" do @@ -38,11 +41,9 @@ RSpec.describe Yookassa::Payment do end describe "#create" do - let(:params) { { payment: File.read("spec/fixtures/payment.json") } } + let(:params) { JSON.parse(File.read("spec/fixtures/payment.json")) } let(:url) { "https://api.yookassa.ru/v3/payments" } - let(:body) { File.read("spec/fixtures/payment_response.json") } - before { stub_request(:any, //).to_return(body: body) } subject { payment.create(payment: params, idempotency_key: idempotency_key) } it "sends a request" do @@ -56,9 +57,7 @@ RSpec.describe Yookassa::Payment do describe "#get_payment_info" do let(:payment_id) { "2490ded1-000f-5000-8000-1f64111bc63e" } let(:url) { "https://api.yookassa.ru/v3/payments/#{payment_id}" } - let(:body) { File.read("spec/fixtures/payment_response.json") } - before { stub_request(:any, //).to_return(body: body) } subject { payment.get_payment_info(payment_id: payment_id) } it "sends a request" do @@ -71,11 +70,8 @@ RSpec.describe Yookassa::Payment do describe "#capture" do let(:payment_id) { "2490ded1-000f-5000-8000-1f64111bc63e" } - let(:params) { { payment: File.read("spec/fixtures/payment.json") } } let(:url) { "https://api.yookassa.ru/v3/payments/#{payment_id}/capture" } - let(:body) { File.read("spec/fixtures/payment_response.json") } - before { stub_request(:any, //).to_return(body: body) } subject { payment.capture(payment_id: payment_id, idempotency_key: idempotency_key) } it "sends a request" do @@ -89,9 +85,7 @@ RSpec.describe Yookassa::Payment do describe "#cancel" do let(:payment_id) { "2490ded1-000f-5000-8000-1f64111bc63e" } let(:url) { "https://api.yookassa.ru/v3/payments/#{payment_id}/cancel" } - let(:body) { File.read("spec/fixtures/payment_response.json") } - before { stub_request(:any, //).to_return(body: body) } subject { payment.cancel(payment_id: payment_id, idempotency_key: idempotency_key) } it "sends a request" do diff --git a/spec/yookassa/refund_spec.rb b/spec/yookassa/refund_spec.rb index b1c6b7c..935a6db 100644 --- a/spec/yookassa/refund_spec.rb +++ b/spec/yookassa/refund_spec.rb @@ -3,7 +3,10 @@ RSpec.describe Yookassa::Refund do let(:settings) { { shop_id: "SHOP_ID", api_key: "API_KEY" } } let(:idempotency_key) { 12_345 } - let(:payment) { described_class.new(settings) } + let(:payment) { described_class.new(**settings) } + let(:body) { File.read("spec/fixtures/refund_response.json") } + + before { stub_request(:any, //).to_return(body: body, headers: { "Content-Type" => "application/json" }) } shared_examples "returns_refund_object" do it "returns success" do @@ -21,11 +24,9 @@ RSpec.describe Yookassa::Refund do end describe "#create" do - let(:payload) { File.read("spec/fixtures/refund.json") } + let(:payload) { JSON.parse(File.read("spec/fixtures/refund.json")) } let(:url) { "https://api.yookassa.ru/v3/refunds" } - let(:body) { File.read("spec/fixtures/refund_response.json") } - before { stub_request(:any, //).to_return(body: body) } subject { payment.create(payload: payload, idempotency_key: idempotency_key) } it "sends a request" do @@ -39,9 +40,7 @@ RSpec.describe Yookassa::Refund do describe "#get_refund_info" do let(:payment_id) { "2490ded1-000f-5000-8000-1f64111bc63e" } let(:url) { "https://api.yookassa.ru/v3/refunds/#{payment_id}" } - let(:body) { File.read("spec/fixtures/refund_response.json") } - before { stub_request(:any, //).to_return(body: body) } subject { payment.get_refund_info(payment_id: payment_id) } it "sends a request" do diff --git a/yookassa.gemspec b/yookassa.gemspec index ef95cd9..181bf86 100644 --- a/yookassa.gemspec +++ b/yookassa.gemspec @@ -22,11 +22,12 @@ Gem::Specification.new do |spec| spec.required_ruby_version = ">= 2.6" - spec.add_runtime_dependency "evil-client", "~> 3.0" + spec.add_runtime_dependency "dry-initializer" + spec.add_runtime_dependency "http", "~> 5.0.1" spec.add_development_dependency "rake", ">= 10.0" spec.add_development_dependency "rspec", "~> 3.5" spec.add_development_dependency "rubocop", "~> 0.71" spec.add_development_dependency "simplecov", "~> 0.16" - spec.add_development_dependency "webmock", "~> 3.5" + spec.add_development_dependency "webmock", "~> 3.14" end