зеркало из
https://github.com/glebtv/yookassa.git
synced 2026-08-28 15:16:18 +03:00
Merge refactor branch
Этот коммит содержится в:
24
.github/workflows/main.yml
поставляемый
24
.github/workflows/main.yml
поставляемый
@@ -2,8 +2,6 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
@@ -14,8 +12,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
ruby:
|
ruby:
|
||||||
- "3.3"
|
|
||||||
- "3.4"
|
- "3.4"
|
||||||
|
- "4.0"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -27,4 +25,22 @@ jobs:
|
|||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
run: bundle exec rubocop
|
run: bundle exec rubocop
|
||||||
- name: Rspec
|
- name: Rspec
|
||||||
run: bundle exec rspec
|
run: bundle exec rspec --tag ~browser
|
||||||
|
|
||||||
|
browser-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: 4.0
|
||||||
|
bundler-cache: true
|
||||||
|
- name: Set up Chrome
|
||||||
|
uses: browser-actions/setup-chrome@v1
|
||||||
|
id: setup-chrome
|
||||||
|
- name: Browser specs (Cuprite)
|
||||||
|
run: bundle exec rspec --tag browser
|
||||||
|
env:
|
||||||
|
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ Layout/LineLength:
|
|||||||
Metrics/BlockLength:
|
Metrics/BlockLength:
|
||||||
AllowedMethods: ["describe", "context", "shared_examples"]
|
AllowedMethods: ["describe", "context", "shared_examples"]
|
||||||
|
|
||||||
|
Metrics/AbcSize:
|
||||||
|
Max: 20
|
||||||
|
|
||||||
|
Metrics/ClassLength:
|
||||||
|
Max: 120
|
||||||
|
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
Max: 15
|
Max: 15
|
||||||
|
|
||||||
|
|||||||
9
Gemfile
9
Gemfile
@@ -16,13 +16,18 @@ group :documentation do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
gem "capybara", "~> 3.40"
|
||||||
|
gem "cuprite", "~> 0.15"
|
||||||
gem "pry", "~> 0.16"
|
gem "pry", "~> 0.16"
|
||||||
gem "pry-byebug", "~> 3.8"
|
gem "pry-byebug", "~> 3.10"
|
||||||
|
gem "rack-test", "~> 2.1"
|
||||||
|
gem "rails", "~> 8.0"
|
||||||
gem "rake", "~> 13.4"
|
gem "rake", "~> 13.4"
|
||||||
gem "rspec", "~> 3.13"
|
gem "rspec", "~> 3.13"
|
||||||
gem "rubocop", "~> 1.88"
|
gem "rubocop", "~> 1.88"
|
||||||
gem "rubocop-rake", "~> 0.6.0"
|
gem "rubocop-rake", "~> 0.7"
|
||||||
gem "rubocop-rspec", "~> 2.31"
|
gem "rubocop-rspec", "~> 2.31"
|
||||||
gem "simplecov", "~> 0.22"
|
gem "simplecov", "~> 0.22"
|
||||||
|
gem "sqlite3", "~> 2.0"
|
||||||
gem "webmock", "~> 3.26"
|
gem "webmock", "~> 3.26"
|
||||||
end
|
end
|
||||||
|
|||||||
387
Gemfile.lock
387
Gemfile.lock
@@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
yookassa (0.2.0)
|
yookassa (0.2.1)
|
||||||
dry-struct
|
dry-struct
|
||||||
dry-validation
|
dry-validation
|
||||||
rs-httpclient (= 3.0.0.beta1)
|
rs-httpclient (= 3.0.0.beta1)
|
||||||
@@ -9,19 +9,111 @@ PATH
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
action_text-trix (2.1.19)
|
||||||
|
railties
|
||||||
|
actioncable (8.1.3)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
websocket-driver (>= 0.6.1)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
actionmailbox (8.1.3)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
activejob (= 8.1.3)
|
||||||
|
activerecord (= 8.1.3)
|
||||||
|
activestorage (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
mail (>= 2.8.0)
|
||||||
|
actionmailer (8.1.3)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
actionview (= 8.1.3)
|
||||||
|
activejob (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
mail (>= 2.8.0)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
actionpack (8.1.3)
|
||||||
|
actionview (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
nokogiri (>= 1.8.5)
|
||||||
|
rack (>= 2.2.4)
|
||||||
|
rack-session (>= 1.0.1)
|
||||||
|
rack-test (>= 0.6.3)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
rails-html-sanitizer (~> 1.6)
|
||||||
|
useragent (~> 0.16)
|
||||||
|
actiontext (8.1.3)
|
||||||
|
action_text-trix (~> 2.1.15)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
activerecord (= 8.1.3)
|
||||||
|
activestorage (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
globalid (>= 0.6.0)
|
||||||
|
nokogiri (>= 1.8.5)
|
||||||
|
actionview (8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
builder (~> 3.1)
|
||||||
|
erubi (~> 1.11)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
rails-html-sanitizer (~> 1.6)
|
||||||
|
activejob (8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
globalid (>= 0.3.6)
|
||||||
|
activemodel (8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
activerecord (8.1.3)
|
||||||
|
activemodel (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
timeout (>= 0.4.0)
|
||||||
|
activestorage (8.1.3)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
activejob (= 8.1.3)
|
||||||
|
activerecord (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
marcel (~> 1.0)
|
||||||
|
activesupport (8.1.3)
|
||||||
|
base64
|
||||||
|
bigdecimal
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||||
|
connection_pool (>= 2.2.5)
|
||||||
|
drb
|
||||||
|
i18n (>= 1.6, < 2)
|
||||||
|
json
|
||||||
|
logger (>= 1.4.2)
|
||||||
|
minitest (>= 5.1)
|
||||||
|
securerandom (>= 0.3)
|
||||||
|
tzinfo (~> 2.0, >= 2.0.5)
|
||||||
|
uri (>= 0.13.1)
|
||||||
addressable (2.9.0)
|
addressable (2.9.0)
|
||||||
public_suffix (>= 2.0.2, < 8.0)
|
public_suffix (>= 2.0.2, < 8.0)
|
||||||
ast (2.4.3)
|
ast (2.4.3)
|
||||||
|
base64 (0.3.0)
|
||||||
bigdecimal (4.1.2)
|
bigdecimal (4.1.2)
|
||||||
|
builder (3.3.0)
|
||||||
byebug (13.0.0)
|
byebug (13.0.0)
|
||||||
reline (>= 0.6.0)
|
reline (>= 0.6.0)
|
||||||
|
capybara (3.40.0)
|
||||||
|
addressable
|
||||||
|
matrix
|
||||||
|
mini_mime (>= 0.1.3)
|
||||||
|
nokogiri (~> 1.11)
|
||||||
|
rack (>= 1.6.0)
|
||||||
|
rack-test (>= 0.6.3)
|
||||||
|
regexp_parser (>= 1.5, < 3.0)
|
||||||
|
xpath (~> 3.2)
|
||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
concurrent-ruby (1.3.7)
|
concurrent-ruby (1.3.7)
|
||||||
|
connection_pool (3.0.2)
|
||||||
crack (1.0.1)
|
crack (1.0.1)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
rexml
|
rexml
|
||||||
|
crass (1.0.7)
|
||||||
|
cuprite (0.17)
|
||||||
|
capybara (~> 3.0)
|
||||||
|
ferrum (~> 0.17.0)
|
||||||
|
date (3.5.1)
|
||||||
diff-lcs (1.6.2)
|
diff-lcs (1.6.2)
|
||||||
docile (1.4.1)
|
docile (1.4.1)
|
||||||
|
drb (2.2.3)
|
||||||
dry-configurable (1.4.0)
|
dry-configurable (1.4.0)
|
||||||
dry-core (~> 1.0)
|
dry-core (~> 1.0)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
@@ -62,19 +154,80 @@ GEM
|
|||||||
dry-initializer (~> 3.2)
|
dry-initializer (~> 3.2)
|
||||||
dry-schema (~> 1.14)
|
dry-schema (~> 1.14)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
|
erb (6.0.4)
|
||||||
|
erubi (1.13.1)
|
||||||
|
ferrum (0.17.2)
|
||||||
|
addressable (~> 2.5)
|
||||||
|
base64 (~> 0.2)
|
||||||
|
concurrent-ruby (~> 1.1)
|
||||||
|
webrick (~> 1.7)
|
||||||
|
websocket-driver (~> 0.7)
|
||||||
|
globalid (1.4.0)
|
||||||
|
activesupport (>= 6.1)
|
||||||
hashdiff (1.2.1)
|
hashdiff (1.2.1)
|
||||||
|
i18n (1.15.2)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
ice_nine (0.11.2)
|
ice_nine (0.11.2)
|
||||||
io-console (0.8.2)
|
io-console (0.8.2)
|
||||||
|
irb (1.18.0)
|
||||||
|
pp (>= 0.6.0)
|
||||||
|
prism (>= 1.3.0)
|
||||||
|
rdoc (>= 4.0.0)
|
||||||
|
reline (>= 0.4.2)
|
||||||
json (2.20.0)
|
json (2.20.0)
|
||||||
language_server-protocol (3.17.0.5)
|
language_server-protocol (3.17.0.5)
|
||||||
lint_roller (1.1.0)
|
lint_roller (1.1.0)
|
||||||
logger (1.7.0)
|
logger (1.7.0)
|
||||||
|
loofah (2.25.1)
|
||||||
|
crass (~> 1.0.2)
|
||||||
|
nokogiri (>= 1.12.0)
|
||||||
|
mail (2.9.1)
|
||||||
|
logger
|
||||||
|
mini_mime (>= 0.1.1)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
|
marcel (1.2.1)
|
||||||
|
matrix (0.4.3)
|
||||||
method_source (1.1.0)
|
method_source (1.1.0)
|
||||||
|
mini_mime (1.1.5)
|
||||||
|
minitest (6.0.6)
|
||||||
|
drb (~> 2.0)
|
||||||
|
prism (~> 1.5)
|
||||||
mutex_m (0.3.0)
|
mutex_m (0.3.0)
|
||||||
|
net-imap (0.6.4.1)
|
||||||
|
date
|
||||||
|
net-protocol
|
||||||
|
net-pop (0.1.2)
|
||||||
|
net-protocol
|
||||||
|
net-protocol (0.2.2)
|
||||||
|
timeout
|
||||||
|
net-smtp (0.5.1)
|
||||||
|
net-protocol
|
||||||
|
nio4r (2.7.5)
|
||||||
|
nokogiri (1.19.4-aarch64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-aarch64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-arm-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-arm-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-arm64-darwin)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-x86_64-darwin)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-x86_64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.19.4-x86_64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
parallel (2.1.0)
|
parallel (2.1.0)
|
||||||
parser (3.3.11.1)
|
parser (3.3.11.1)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
|
pp (0.6.4)
|
||||||
|
prettyprint
|
||||||
|
prettyprint (0.2.0)
|
||||||
prism (1.9.0)
|
prism (1.9.0)
|
||||||
pry (0.16.0)
|
pry (0.16.0)
|
||||||
coderay (~> 1.1)
|
coderay (~> 1.1)
|
||||||
@@ -86,8 +239,54 @@ GEM
|
|||||||
public_suffix (7.0.5)
|
public_suffix (7.0.5)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (3.2.6)
|
rack (3.2.6)
|
||||||
|
rack-session (2.1.2)
|
||||||
|
base64 (>= 0.1.0)
|
||||||
|
rack (>= 3.0.0)
|
||||||
|
rack-test (2.2.0)
|
||||||
|
rack (>= 1.3)
|
||||||
|
rackup (2.3.1)
|
||||||
|
rack (>= 3)
|
||||||
|
rails (8.1.3)
|
||||||
|
actioncable (= 8.1.3)
|
||||||
|
actionmailbox (= 8.1.3)
|
||||||
|
actionmailer (= 8.1.3)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
actiontext (= 8.1.3)
|
||||||
|
actionview (= 8.1.3)
|
||||||
|
activejob (= 8.1.3)
|
||||||
|
activemodel (= 8.1.3)
|
||||||
|
activerecord (= 8.1.3)
|
||||||
|
activestorage (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
bundler (>= 1.15.0)
|
||||||
|
railties (= 8.1.3)
|
||||||
|
rails-dom-testing (2.3.0)
|
||||||
|
activesupport (>= 5.0.0)
|
||||||
|
minitest
|
||||||
|
nokogiri (>= 1.6)
|
||||||
|
rails-html-sanitizer (1.7.0)
|
||||||
|
loofah (~> 2.25)
|
||||||
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||||
|
railties (8.1.3)
|
||||||
|
actionpack (= 8.1.3)
|
||||||
|
activesupport (= 8.1.3)
|
||||||
|
irb (~> 1.13)
|
||||||
|
rackup (>= 1.0.0)
|
||||||
|
rake (>= 12.2)
|
||||||
|
thor (~> 1.0, >= 1.2.2)
|
||||||
|
tsort (>= 0.2)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.4.2)
|
rake (13.4.2)
|
||||||
|
rbs (4.0.3)
|
||||||
|
logger
|
||||||
|
prism (>= 1.6.0)
|
||||||
|
tsort
|
||||||
|
rdoc (8.0.0)
|
||||||
|
erb
|
||||||
|
prism (>= 1.6.0)
|
||||||
|
rbs (>= 4.0.0)
|
||||||
|
tsort
|
||||||
regexp_parser (2.12.0)
|
regexp_parser (2.12.0)
|
||||||
reline (0.6.3)
|
reline (0.6.3)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
@@ -127,8 +326,9 @@ GEM
|
|||||||
rubocop-factory_bot (2.28.0)
|
rubocop-factory_bot (2.28.0)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (~> 1.72, >= 1.72.1)
|
||||||
rubocop-rake (0.6.0)
|
rubocop-rake (0.7.1)
|
||||||
rubocop (~> 1.0)
|
lint_roller (~> 1.1)
|
||||||
|
rubocop (>= 1.72.1)
|
||||||
rubocop-rspec (2.31.0)
|
rubocop-rspec (2.31.0)
|
||||||
rubocop (~> 1.40)
|
rubocop (~> 1.40)
|
||||||
rubocop-capybara (~> 2.17)
|
rubocop-capybara (~> 2.17)
|
||||||
@@ -137,41 +337,216 @@ GEM
|
|||||||
rubocop-rspec_rails (2.29.1)
|
rubocop-rspec_rails (2.29.1)
|
||||||
rubocop (~> 1.61)
|
rubocop (~> 1.61)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
|
securerandom (0.4.1)
|
||||||
simplecov (0.22.0)
|
simplecov (0.22.0)
|
||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
simplecov-html (~> 0.11)
|
simplecov-html (~> 0.11)
|
||||||
simplecov_json_formatter (~> 0.1)
|
simplecov_json_formatter (~> 0.1)
|
||||||
simplecov-html (0.13.2)
|
simplecov-html (0.13.2)
|
||||||
simplecov_json_formatter (0.1.4)
|
simplecov_json_formatter (0.1.4)
|
||||||
|
sqlite3 (2.9.5-aarch64-linux-gnu)
|
||||||
|
sqlite3 (2.9.5-aarch64-linux-musl)
|
||||||
|
sqlite3 (2.9.5-arm-linux-gnu)
|
||||||
|
sqlite3 (2.9.5-arm-linux-musl)
|
||||||
|
sqlite3 (2.9.5-arm64-darwin)
|
||||||
|
sqlite3 (2.9.5-x86_64-darwin)
|
||||||
|
sqlite3 (2.9.5-x86_64-linux-gnu)
|
||||||
|
sqlite3 (2.9.5-x86_64-linux-musl)
|
||||||
|
thor (1.5.0)
|
||||||
|
timeout (0.6.1)
|
||||||
|
tsort (0.2.0)
|
||||||
|
tzinfo (2.0.6)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (3.2.0)
|
unicode-display_width (3.2.0)
|
||||||
unicode-emoji (~> 4.1)
|
unicode-emoji (~> 4.1)
|
||||||
unicode-emoji (4.2.0)
|
unicode-emoji (4.2.0)
|
||||||
|
uri (1.1.1)
|
||||||
|
useragent (0.16.11)
|
||||||
webmock (3.26.2)
|
webmock (3.26.2)
|
||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
webrick (1.9.2)
|
webrick (1.9.2)
|
||||||
|
websocket-driver (0.8.2)
|
||||||
|
base64
|
||||||
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-extensions (0.1.5)
|
||||||
|
xpath (3.2.0)
|
||||||
|
nokogiri (~> 1.8)
|
||||||
yard (0.9.44)
|
yard (0.9.44)
|
||||||
zeitwerk (2.8.2)
|
zeitwerk (2.8.2)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
aarch64-linux-gnu
|
||||||
|
aarch64-linux-musl
|
||||||
|
arm-linux-gnu
|
||||||
|
arm-linux-musl
|
||||||
|
arm64-darwin
|
||||||
|
x86_64-darwin
|
||||||
|
x86_64-linux-gnu
|
||||||
|
x86_64-linux-musl
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
capybara (~> 3.40)
|
||||||
|
cuprite (~> 0.15)
|
||||||
pry (~> 0.16)
|
pry (~> 0.16)
|
||||||
pry-byebug (~> 3.8)
|
pry-byebug (~> 3.10)
|
||||||
rack (~> 3.2)
|
rack (~> 3.2)
|
||||||
|
rack-test (~> 2.1)
|
||||||
|
rails (~> 8.0)
|
||||||
rake (~> 13.4)
|
rake (~> 13.4)
|
||||||
rs-httpclient (= 3.0.0.beta1)
|
rs-httpclient (= 3.0.0.beta1)
|
||||||
rspec (~> 3.13)
|
rspec (~> 3.13)
|
||||||
rubocop (~> 1.88)
|
rubocop (~> 1.88)
|
||||||
rubocop-rake (~> 0.6.0)
|
rubocop-rake (~> 0.7)
|
||||||
rubocop-rspec (~> 2.31)
|
rubocop-rspec (~> 2.31)
|
||||||
simplecov (~> 0.22)
|
simplecov (~> 0.22)
|
||||||
|
sqlite3 (~> 2.0)
|
||||||
webmock (~> 3.26)
|
webmock (~> 3.26)
|
||||||
webrick (~> 1.9)
|
webrick (~> 1.9)
|
||||||
yard (~> 0.9)
|
yard (~> 0.9)
|
||||||
yookassa!
|
yookassa!
|
||||||
|
|
||||||
|
CHECKSUMS
|
||||||
|
action_text-trix (2.1.19) sha256=7012f59421009cf284aa651294896414d653a61a2417c9b8714c8476d2f74009
|
||||||
|
actioncable (8.1.3) sha256=e5bc7f75e44e6a22de29c4f43176927c3a9ce4824464b74ed18d8226e75a80f0
|
||||||
|
actionmailbox (8.1.3) sha256=df7da474eaa0e70df4ed5a6fef66eb3b3b0f2dbf7f14518deee8d77f1b4aae59
|
||||||
|
actionmailer (8.1.3) sha256=831f724891bb70d0aaa4d76581a6321124b6a752cb655c9346aae5479318448d
|
||||||
|
actionpack (8.1.3) sha256=af998cae4d47c5d581a2cc363b5c77eb718b7c4b45748d81b1887b25621c29a3
|
||||||
|
actiontext (8.1.3) sha256=d291019c00e1ea9e6463011fa214f6081a56d7b9a1d224e7d3f6384c1dafc7d2
|
||||||
|
actionview (8.1.3) sha256=1347c88c7f3edb38100c5ce0e9fb5e62d7755f3edc1b61cce2eb0b2c6ea2fd5d
|
||||||
|
activejob (8.1.3) sha256=a149b1766aa8204c3c3da7309e4becd40fcd5529c348cffbf6c9b16b565fe8d3
|
||||||
|
activemodel (8.1.3) sha256=90c05cbe4cef3649b8f79f13016191ea94c4525ce4a5c0fb7ef909c4b91c8219
|
||||||
|
activerecord (8.1.3) sha256=8003be7b2466ba0a2a670e603eeb0a61dd66058fccecfc49901e775260ac70ab
|
||||||
|
activestorage (8.1.3) sha256=0564ce9309143951a67615e1bb4e090ee54b8befed417133cae614479b46384d
|
||||||
|
activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e
|
||||||
|
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
||||||
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
||||||
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
||||||
|
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
||||||
|
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
||||||
|
bundler (4.0.11) sha256=5bcec0fb78302e48d02ee46f10ee6e6942be647ba5b44a6d1ddfda9a240ce785
|
||||||
|
byebug (13.0.0) sha256=d2263efe751941ca520fa29744b71972d39cbc41839496706f5d9b22e92ae05d
|
||||||
|
capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef
|
||||||
|
coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b
|
||||||
|
concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0
|
||||||
|
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
||||||
|
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
|
||||||
|
crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
|
||||||
|
cuprite (0.17) sha256=b140d5dc70d08b97ad54bcf45cd95d0bd430e291e9dffe76fff851fddd57c12b
|
||||||
|
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
|
||||||
|
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
||||||
|
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
||||||
|
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
||||||
|
dry-configurable (1.4.0) sha256=e35d1b5f3c081753ef361f564919db79000f32cfa6f20ee3a3ba5921b41b73ce
|
||||||
|
dry-core (1.2.0) sha256=0cc5a7da88df397f153947eeeae42e876e999c1e30900f3c536fb173854e96a1
|
||||||
|
dry-inflector (1.3.1) sha256=7fb0c2bb04f67638f25c52e7ba39ab435d922a3a5c3cd196120f63accb682dcc
|
||||||
|
dry-initializer (3.2.0) sha256=37d59798f912dc0a1efe14a4db4a9306989007b302dcd5f25d0a2a20c166c4e3
|
||||||
|
dry-logic (1.6.0) sha256=da6fedbc0f90fc41f9b0cc7e6f05f5d529d1efaef6c8dcc8e0733f685745cea2
|
||||||
|
dry-schema (1.16.0) sha256=cd3aaeabc0f1af66ec82a29096d4c4fb92a0a58b9dae29a22b1bbceb78985727
|
||||||
|
dry-struct (1.8.1) sha256=033868594c45241540172bf1ebbc8bb76b72b4f0717072325deba38ac13e80f1
|
||||||
|
dry-types (1.9.1) sha256=baebeecdb9f8395d6c9d227b62011279440943e3ef2468fe8ccc1ba11467f178
|
||||||
|
dry-validation (1.11.1) sha256=70900bb5a2d911c8aab566d3e360c6bff389b8bf92ea8e04885ce51c41ff8085
|
||||||
|
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
|
||||||
|
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
||||||
|
ferrum (0.17.2) sha256=2c2540a850b211a46f4d81de21bfd62048f507e4c327d1807225c3823c17e6ee
|
||||||
|
globalid (1.4.0) sha256=037f12fbf1d9d7a014d501c2d5c77356fd4ddd96d7a7991d6700bba96706f427
|
||||||
|
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
|
||||||
|
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
|
||||||
|
ice_nine (0.11.2) sha256=5d506a7d2723d5592dc121b9928e4931742730131f22a1a37649df1c1e2e63db
|
||||||
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
||||||
|
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
||||||
|
json (2.20.0) sha256=9362bc6e55a952b056abf9167cf053358181c904cb70cd6eee0808ea830fc32b
|
||||||
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
||||||
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
||||||
|
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
||||||
|
loofah (2.25.1) sha256=d436c73dbd0c1147b16c4a41db097942d217303e1f7728704b37e4df9f6d2e04
|
||||||
|
mail (2.9.1) sha256=06574eca475253d6c18145dd70af80d0eb970182d55053497c5f4d797ea160e8
|
||||||
|
marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f
|
||||||
|
matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b
|
||||||
|
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
|
||||||
|
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
||||||
|
minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
|
||||||
|
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
|
||||||
|
net-imap (0.6.4.1) sha256=29f0360d75a7efd3539f16ac1957dea5c0a51ddeceb348db4553c3120914ea0d
|
||||||
|
net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
|
||||||
|
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
|
||||||
|
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
|
||||||
|
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
|
||||||
|
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
|
||||||
|
nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af
|
||||||
|
nokogiri (1.19.4-arm-linux-gnu) sha256=a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca
|
||||||
|
nokogiri (1.19.4-arm-linux-musl) sha256=588923c101bcfa78869734d247d25b598674323e7f22474fc468f6e5647311eb
|
||||||
|
nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527
|
||||||
|
nokogiri (1.19.4-x86_64-darwin) sha256=7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551
|
||||||
|
nokogiri (1.19.4-x86_64-linux-gnu) sha256=379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a
|
||||||
|
nokogiri (1.19.4-x86_64-linux-musl) sha256=17dfb7c1fa194ae02fbf7c51a7afc8d278045ab3fdacfd86f91d02d7b274470b
|
||||||
|
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
||||||
|
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
|
||||||
|
pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
|
||||||
|
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
||||||
|
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
||||||
|
pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
|
||||||
|
pry-byebug (3.12.0) sha256=594e094ae8a8390a7ad4c7b36ae36e13304ed02664c67417d108dc5f7213d1b7
|
||||||
|
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
||||||
|
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
||||||
|
rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2
|
||||||
|
rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
|
||||||
|
rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
|
||||||
|
rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
|
||||||
|
rails (8.1.3) sha256=6d017ba5348c98fc909753a8169b21d44de14d2a0b92d140d1a966834c3c9cd3
|
||||||
|
rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
|
||||||
|
rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89
|
||||||
|
railties (8.1.3) sha256=913eb0e0cb520aac687ffd74916bd726d48fa21f47833c6292576ef6a286de22
|
||||||
|
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
||||||
|
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
||||||
|
rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14
|
||||||
|
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
|
||||||
|
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
||||||
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
||||||
|
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
||||||
|
rs-httpclient (3.0.0.beta1) sha256=6b4838355d9c30c5f70529e35d1a1e21628473c3bb4ba14a17814ecdb6ea81ea
|
||||||
|
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
||||||
|
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
||||||
|
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
||||||
|
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
|
||||||
|
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
||||||
|
rubocop (1.88.1) sha256=726af773d6bc169ed3ff852f3ca020b7c58d39c34e7a8d879a8e0147cc994f26
|
||||||
|
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
||||||
|
rubocop-capybara (2.23.0) sha256=f9ea1ba3a7561ee8e88cf76fc378ce517ce5327155f305ee7b5c2500e5aee357
|
||||||
|
rubocop-factory_bot (2.28.0) sha256=4b17fc02124444173317e131759d195b0d762844a71a29fe8139c1105d92f0cb
|
||||||
|
rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
|
||||||
|
rubocop-rspec (2.31.0) sha256=2bae19388d78e1ceace44cd95fd34f3209f4ef20cac1b168d0a1325cbba3d672
|
||||||
|
rubocop-rspec_rails (2.29.1) sha256=4ae95abbe9ca5a9b6d8be14e50d230fb5b6ba033b05d4c0981b5b76fc44988e4
|
||||||
|
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
||||||
|
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
||||||
|
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
|
||||||
|
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
|
||||||
|
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
|
||||||
|
sqlite3 (2.9.5-aarch64-linux-gnu) sha256=78075b6337d3d182c6d2b4691049ed45cd220826160c9ea18946bf6a1de200dc
|
||||||
|
sqlite3 (2.9.5-aarch64-linux-musl) sha256=18c801185deb4adc01ddb281e8f672a39e3d1729979ca91e39439cd3eac0402d
|
||||||
|
sqlite3 (2.9.5-arm-linux-gnu) sha256=1bdfca0c7d63998c60b0f4a8e3c8df2d33800ccc4abd2d612eddbbbc92a4c48b
|
||||||
|
sqlite3 (2.9.5-arm-linux-musl) sha256=bae1109d12b2e9f588455967729b008e1ff4feb7761749df695019c9079913c6
|
||||||
|
sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2
|
||||||
|
sqlite3 (2.9.5-x86_64-darwin) sha256=8e9caae38bd7ebb29cbeee3e7ab1d12dc2327d9a1b92c7fcf0dda05589627a81
|
||||||
|
sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e
|
||||||
|
sqlite3 (2.9.5-x86_64-linux-musl) sha256=e7d3a7474e8af0f96150c21abc203fbab5437206bfcdf11deab7741c0ca516f2
|
||||||
|
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
||||||
|
timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
|
||||||
|
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
||||||
|
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
||||||
|
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
||||||
|
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
||||||
|
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
||||||
|
useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
|
||||||
|
webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
|
||||||
|
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
|
||||||
|
websocket-driver (0.8.2) sha256=97c556b019bf3410b4961002ac501621e9322d3f8a7bc02161a09301cc4c4146
|
||||||
|
websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
|
||||||
|
xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e
|
||||||
|
yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
|
||||||
|
yookassa (0.2.1)
|
||||||
|
zeitwerk (2.8.2) sha256=7212a61311083c604184b1ea2574b9aa05cd14f855a0841c06985cabe9181d12
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
4.0.11
|
4.0.11
|
||||||
|
|||||||
71
README.md
71
README.md
@@ -80,6 +80,77 @@ Yookassa.payments.capture(payment_id: '12345')
|
|||||||
Yookassa.payments.cancel(payment_id: '12345')
|
Yookassa.payments.cancel(payment_id: '12345')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Rails webhook endpoint (engine)
|
||||||
|
|
||||||
|
The gem ships with a Rails engine and a default webhook controller you can use directly.
|
||||||
|
|
||||||
|
1) Configure the webhook token and (optionally) allowed source IPs:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/initializers/yookassa.rb
|
||||||
|
Yookassa.configure do |config|
|
||||||
|
config.shop_id = ENV.fetch('YOOKASSA_SHOP_ID')
|
||||||
|
config.api_key = ENV.fetch('YOOKASSA_API_KEY')
|
||||||
|
|
||||||
|
# Random, long, secret token used in webhook URL path.
|
||||||
|
config.webhook_token = ENV.fetch('YOOKASSA_WEBHOOK_TOKEN')
|
||||||
|
|
||||||
|
# Optional override. Defaults come from YooKassa docs:
|
||||||
|
# https://yookassa.ru/developers/using-api/webhooks#ip
|
||||||
|
# config.webhook_allowed_ips = ['185.71.76.0/27', ...]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
2) Mount the engine in routes:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/routes.rb
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
mount Yookassa::Engine => '/yookassa'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
This exposes:
|
||||||
|
|
||||||
|
- `POST /yookassa/webhooks/:token`
|
||||||
|
|
||||||
|
Set your YooKassa webhook URL to include the real token value, for example:
|
||||||
|
|
||||||
|
- `https://example.com/yookassa/webhooks/<long-random-token>`
|
||||||
|
|
||||||
|
The default `Yookassa::WebhooksController` verifies:
|
||||||
|
|
||||||
|
- token in URL path
|
||||||
|
- request source `request.remote_ip` is in allowlist
|
||||||
|
- webhook object matches fresh API fetch by `id` and `status`
|
||||||
|
|
||||||
|
No signature headers are used.
|
||||||
|
|
||||||
|
### Overriding webhook handling
|
||||||
|
|
||||||
|
In host app, inherit from the gem controller and implement business logic in `process_webhook`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/controllers/my_yookassa_webhooks_controller.rb
|
||||||
|
class MyYookassaWebhooksController < Yookassa::WebhooksController
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_webhook(payload)
|
||||||
|
object = payload['object'] || payload.dig('data', 'object')
|
||||||
|
return unless object
|
||||||
|
|
||||||
|
# Your app-specific processing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Then route to your controller (keeping token in path):
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/routes.rb
|
||||||
|
post '/webhooks/yookassa/:token', to: 'my_yookassa_webhooks#create'
|
||||||
|
```
|
||||||
|
|
||||||
### Path to 1.0
|
### Path to 1.0
|
||||||
|
|
||||||
**Настройки SDK API ЮKassa**
|
**Настройки SDK API ЮKassa**
|
||||||
|
|||||||
147
app/controllers/yookassa/webhooks_controller.rb
Обычный файл
147
app/controllers/yookassa/webhooks_controller.rb
Обычный файл
@@ -0,0 +1,147 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "ipaddr"
|
||||||
|
require "json"
|
||||||
|
|
||||||
|
module Yookassa
|
||||||
|
class WebhooksController < ::ActionController::Base
|
||||||
|
skip_before_action :verify_authenticity_token, raise: false
|
||||||
|
|
||||||
|
def create
|
||||||
|
payload = parsed_payload
|
||||||
|
return head :unauthorized unless authentic_webhook?(payload)
|
||||||
|
|
||||||
|
process_webhook(payload)
|
||||||
|
head :ok
|
||||||
|
rescue JSON::ParserError
|
||||||
|
head :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_webhook(_payload)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def authentic_webhook?(payload)
|
||||||
|
token_ok = token_valid?
|
||||||
|
ip_ok = source_ip_allowed?
|
||||||
|
api_ok = payload_matches_api_object?(payload)
|
||||||
|
|
||||||
|
unless token_ok && ip_ok && api_ok
|
||||||
|
Rails.logger.info("[Yookassa Webhook] Auth failed: token=#{token_ok}, ip=#{ip_ok}, api=#{api_ok}, remote_ip=#{request.remote_ip}")
|
||||||
|
end
|
||||||
|
|
||||||
|
token_ok && ip_ok && api_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
def token_valid?
|
||||||
|
token = params[:token].to_s
|
||||||
|
configured_token = Yookassa.config.webhook_token.to_s
|
||||||
|
if token.empty? || configured_token.empty?
|
||||||
|
log_missing_token(token, configured_token)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if token.bytesize != configured_token.bytesize
|
||||||
|
Rails.logger.info("[Yookassa Webhook] Token comparison: false (length mismatch)")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
result = ActiveSupport::SecurityUtils.secure_compare(token, configured_token)
|
||||||
|
Rails.logger.info("[Yookassa Webhook] Token comparison: #{result}")
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_missing_token(token, configured_token)
|
||||||
|
param_state = token.empty? ? "empty" : "present"
|
||||||
|
configured_state = configured_token.empty? ? "empty" : "present"
|
||||||
|
|
||||||
|
Rails.logger.info(
|
||||||
|
"[Yookassa Webhook] Token validation failed: " \
|
||||||
|
"param_token=#{param_state}, configured_token=#{configured_state}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_ip_allowed?
|
||||||
|
source_ip = request.remote_ip
|
||||||
|
allowed = allowed_cidrs.any? { |cidr| IPAddr.new(cidr).include?(source_ip) }
|
||||||
|
Rails.logger.info("[Yookassa Webhook] IP check: remote_ip=#{source_ip}, allowed=#{allowed}, cidrs=#{allowed_cidrs}")
|
||||||
|
allowed
|
||||||
|
rescue IPAddr::InvalidAddressError => e
|
||||||
|
Rails.logger.info("[Yookassa Webhook] IP check failed: #{e.message}")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def allowed_cidrs
|
||||||
|
ips = Yookassa.config.webhook_allowed_ips
|
||||||
|
return Yookassa::Config::DEFAULT_WEBHOOK_ALLOWED_IPS if ips.nil? || ips.empty?
|
||||||
|
|
||||||
|
ips
|
||||||
|
end
|
||||||
|
|
||||||
|
def parsed_payload
|
||||||
|
@parsed_payload ||= JSON.parse(request.raw_post)
|
||||||
|
end
|
||||||
|
|
||||||
|
def payload_matches_api_object?(payload)
|
||||||
|
object = extract_object(payload)
|
||||||
|
return false unless valid_payload_object?(object)
|
||||||
|
|
||||||
|
object_id = object["id"].to_s
|
||||||
|
object_status = object["status"].to_s
|
||||||
|
return false unless valid_object_identity?(object_id, object_status)
|
||||||
|
|
||||||
|
fetched_object = fetch_object_from_api(payload, object_id)
|
||||||
|
return log_missing_api_object(object_id) unless fetched_object
|
||||||
|
|
||||||
|
result = fetched_object.id == object_id && fetched_object.status == object_status
|
||||||
|
log_api_object_match(fetched_object, result)
|
||||||
|
result
|
||||||
|
rescue StandardError => e
|
||||||
|
Rails.logger.info("[Yookassa Webhook] API object check failed: #{e.message}")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_payload_object?(object)
|
||||||
|
return true if object.is_a?(Hash)
|
||||||
|
|
||||||
|
Rails.logger.info("[Yookassa Webhook] API object check failed: object is not a Hash")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_object_identity?(object_id, object_status)
|
||||||
|
return true unless object_id.empty? || object_status.empty?
|
||||||
|
|
||||||
|
Rails.logger.info("[Yookassa Webhook] API object check failed: object_id=#{object_id}, object_status=#{object_status}")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_missing_api_object(object_id)
|
||||||
|
Rails.logger.info("[Yookassa Webhook] API object check failed: could not fetch object #{object_id}")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_api_object_match(fetched_object, result)
|
||||||
|
Rails.logger.info(
|
||||||
|
"[Yookassa Webhook] API object check: fetched_id=#{fetched_object.id}, " \
|
||||||
|
"fetched_status=#{fetched_object.status}, matches=#{result}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_object(payload)
|
||||||
|
payload["object"] || payload.dig("data", "object")
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_object_from_api(payload, object_id)
|
||||||
|
event_name = payload["event"].to_s
|
||||||
|
event_name = payload["type"].to_s if event_name.empty?
|
||||||
|
|
||||||
|
if event_name.start_with?("payment.")
|
||||||
|
Yookassa.payments.find(payment_id: object_id)
|
||||||
|
elsif event_name.start_with?("refund.")
|
||||||
|
Yookassa.refunds.find(payment_id: object_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
5
config/routes.rb
Обычный файл
5
config/routes.rb
Обычный файл
@@ -0,0 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Yookassa::Engine.routes.draw do
|
||||||
|
post "/webhooks/:token", to: "webhooks#create"
|
||||||
|
end
|
||||||
24
docs/changelog.md
Обычный файл
24
docs/changelog.md
Обычный файл
@@ -0,0 +1,24 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Rails engine integration for webhook handling (`Yookassa::Engine`).
|
||||||
|
- Default webhook controller (`Yookassa::WebhooksController`) with built-in authenticity checks.
|
||||||
|
- Config options for webhook security:
|
||||||
|
- `webhook_token` (secret URL token in path)
|
||||||
|
- `webhook_allowed_ips` (YooKassa source allowlist, overridable)
|
||||||
|
- RSpec coverage for webhook controller behavior.
|
||||||
|
- Cuprite browser spec for real browser webhook request flow.
|
||||||
|
- CI job for browser tests in GitHub Actions.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Documentation updated to use gem-based webhook integration instead of app-side hand-rolled service/controller code.
|
||||||
|
- Webhook security guidance switched to YooKassa-documented approach:
|
||||||
|
- secret URL token
|
||||||
|
- source IP allowlist
|
||||||
|
- API re-fetch and object/status comparison
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
- No webhook signature header validation is implemented because YooKassa webhook docs describe authenticity checks via source IP and object status verification.
|
||||||
|
- Source for default IP ranges: https://yookassa.ru/developers/using-api/webhooks#ip
|
||||||
135
docs/chaos-yookassa.md
Обычный файл
135
docs/chaos-yookassa.md
Обычный файл
@@ -0,0 +1,135 @@
|
|||||||
|
# YooKassa Smart Payment Integration Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide uses the `yookassa` gem for both API calls and webhook security handling.
|
||||||
|
Do not re-implement webhook signature checks in your app: this integration uses a
|
||||||
|
secret URL token + source IP allowlist + API re-fetch verification.
|
||||||
|
|
||||||
|
## Payment flow
|
||||||
|
|
||||||
|
```
|
||||||
|
User taps "Pay" -> backend creates payment via gem -> user pays on YooKassa page ->
|
||||||
|
YooKassa sends webhook -> gem controller verifies webhook -> app applies business logic
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1) Install and configure the gem
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Gemfile
|
||||||
|
gem "yookassa"
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/initializers/yookassa.rb
|
||||||
|
Yookassa.configure do |config|
|
||||||
|
config.shop_id = ENV.fetch("YOOKASSA_SHOP_ID")
|
||||||
|
config.api_key = ENV.fetch("YOOKASSA_SECRET_KEY")
|
||||||
|
|
||||||
|
# Required: long random token used in webhook route path.
|
||||||
|
config.webhook_token = ENV.fetch("YOOKASSA_WEBHOOK_TOKEN")
|
||||||
|
|
||||||
|
# Optional override, if you need a custom list.
|
||||||
|
# Defaults are from YooKassa docs:
|
||||||
|
# https://yookassa.ru/developers/using-api/webhooks#ip
|
||||||
|
# config.webhook_allowed_ips = ["185.71.76.0/27", ...]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2) Create payments with gem client
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
payload = {
|
||||||
|
amount: {
|
||||||
|
value: "100.00",
|
||||||
|
currency: "RUB"
|
||||||
|
},
|
||||||
|
capture: true,
|
||||||
|
confirmation: {
|
||||||
|
type: "redirect",
|
||||||
|
return_url: "https://example.com/payment/return"
|
||||||
|
},
|
||||||
|
description: "Order 123"
|
||||||
|
}
|
||||||
|
|
||||||
|
payment = Yookassa.payments.create(payment: payload)
|
||||||
|
confirmation_url = payment.confirmation.confirmation_url
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3) Mount gem webhook engine
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/routes.rb
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
mount Yookassa::Engine => "/yookassa"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Default webhook endpoint:
|
||||||
|
|
||||||
|
- `POST /yookassa/webhooks/:token`
|
||||||
|
|
||||||
|
Your YooKassa dashboard webhook URL must include the configured token value, for example:
|
||||||
|
|
||||||
|
- `https://your-domain.com/yookassa/webhooks/<long-random-token>`
|
||||||
|
|
||||||
|
## 4) Add app-specific webhook logic by inheritance
|
||||||
|
|
||||||
|
`Yookassa::WebhooksController` handles authenticity checks. Override only processing.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# app/controllers/yookassa_events_controller.rb
|
||||||
|
class YookassaEventsController < Yookassa::WebhooksController
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_webhook(payload)
|
||||||
|
event = payload["event"] || payload["type"]
|
||||||
|
object = payload["object"] || payload.dig("data", "object")
|
||||||
|
return unless object
|
||||||
|
|
||||||
|
case event
|
||||||
|
when "payment.succeeded"
|
||||||
|
# apply balance / mark paid in your app
|
||||||
|
when "payment.canceled"
|
||||||
|
# mark failed in your app
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/routes.rb
|
||||||
|
post "/webhooks/yookassa/:token", to: "yookassa_events#create"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Webhook security model used by gem
|
||||||
|
|
||||||
|
The default controller accepts webhook only if all checks pass:
|
||||||
|
|
||||||
|
1. route includes token and it matches `Yookassa.config.webhook_token`
|
||||||
|
2. source address from `request.remote_ip` is in allowlist
|
||||||
|
3. webhook object `id` + `status` matches a fresh API fetch
|
||||||
|
|
||||||
|
This approach intentionally does not use HMAC signature headers.
|
||||||
|
|
||||||
|
## Defaults and allowlist override
|
||||||
|
|
||||||
|
Default allowed IP ranges in gem:
|
||||||
|
|
||||||
|
- `185.71.76.0/27`
|
||||||
|
- `185.71.77.0/27`
|
||||||
|
- `77.75.153.0/25`
|
||||||
|
- `77.75.156.11`
|
||||||
|
- `77.75.156.35`
|
||||||
|
- `77.75.154.128/25`
|
||||||
|
- `2a02:5180::/32`
|
||||||
|
|
||||||
|
Source: https://yookassa.ru/developers/using-api/webhooks#ip
|
||||||
|
|
||||||
|
If needed:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Yookassa.configure do |config|
|
||||||
|
config.webhook_allowed_ips = ["203.0.113.10"]
|
||||||
|
end
|
||||||
|
```
|
||||||
70
docs/webhooks.md
Обычный файл
70
docs/webhooks.md
Обычный файл
@@ -0,0 +1,70 @@
|
|||||||
|
# Webhooks
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The gem provides a Rails engine endpoint for YooKassa webhooks and a default controller
|
||||||
|
that performs security checks before running your app-specific logic.
|
||||||
|
|
||||||
|
Default endpoint after mounting engine:
|
||||||
|
|
||||||
|
- `POST /yookassa/webhooks/:token`
|
||||||
|
|
||||||
|
## Security model
|
||||||
|
|
||||||
|
The controller accepts a webhook only if all checks pass:
|
||||||
|
|
||||||
|
1. Route token matches configured `Yookassa.config.webhook_token`.
|
||||||
|
2. Source IP from `request.remote_ip` belongs to allowed CIDRs.
|
||||||
|
3. Webhook object `id` and `status` match a fresh API fetch (`payments.find` or `refunds.find`).
|
||||||
|
|
||||||
|
This follows YooKassa webhook guidance:
|
||||||
|
|
||||||
|
- https://yookassa.ru/developers/using-api/webhooks
|
||||||
|
|
||||||
|
## Default allowed IP ranges
|
||||||
|
|
||||||
|
From YooKassa docs (source: https://yookassa.ru/developers/using-api/webhooks#ip):
|
||||||
|
|
||||||
|
- `185.71.76.0/27`
|
||||||
|
- `185.71.77.0/27`
|
||||||
|
- `77.75.153.0/25`
|
||||||
|
- `77.75.156.11`
|
||||||
|
- `77.75.156.35`
|
||||||
|
- `77.75.154.128/25`
|
||||||
|
- `2a02:5180::/32`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Yookassa.configure do |config|
|
||||||
|
config.shop_id = ENV.fetch("YOOKASSA_SHOP_ID")
|
||||||
|
config.api_key = ENV.fetch("YOOKASSA_API_KEY")
|
||||||
|
config.webhook_token = ENV.fetch("YOOKASSA_WEBHOOK_TOKEN")
|
||||||
|
|
||||||
|
# Optional override
|
||||||
|
# config.webhook_allowed_ips = ["203.0.113.10"]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Overriding controller behavior
|
||||||
|
|
||||||
|
Inherit from `Yookassa::WebhooksController` and override `process_webhook`.
|
||||||
|
All security checks remain in the base controller.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class MyYookassaWebhooksController < Yookassa::WebhooksController
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_webhook(payload)
|
||||||
|
event = payload["event"]
|
||||||
|
object = payload["object"]
|
||||||
|
# app-specific logic
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Route example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
post "/webhooks/yookassa/:token", to: "my_yookassa_webhooks#create"
|
||||||
|
```
|
||||||
@@ -7,6 +7,7 @@ require "yookassa/config"
|
|||||||
require "yookassa/payments"
|
require "yookassa/payments"
|
||||||
require "yookassa/refunds"
|
require "yookassa/refunds"
|
||||||
require "yookassa/receipts"
|
require "yookassa/receipts"
|
||||||
|
require "yookassa/webhooks"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class << self
|
class << self
|
||||||
@@ -31,5 +32,11 @@ module Yookassa
|
|||||||
def receipts
|
def receipts
|
||||||
@receipts ||= Receipts.new
|
@receipts ||= Receipts.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def webhooks
|
||||||
|
@webhooks ||= Webhooks.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require "yookassa/railtie" if defined?(Rails::Railtie)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
require "httpclient"
|
require "httpclient"
|
||||||
require "json"
|
require "json"
|
||||||
require_relative "./entity/error"
|
require_relative "entity/error"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Client
|
class Client
|
||||||
@@ -38,8 +38,9 @@ module Yookassa
|
|||||||
api_call { http.post("#{API_URL}#{endpoint}", body: JSON.generate(payload), header: headers) }
|
api_call { http.post("#{API_URL}#{endpoint}", body: JSON.generate(payload), header: headers) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(endpoint, idempotency_key:)
|
def delete(endpoint, idempotency_key: nil)
|
||||||
api_call { http.delete("#{API_URL}#{endpoint}", header: { "Idempotence-Key" => idempotency_key }) }
|
headers = idempotency_key ? { "Idempotence-Key" => idempotency_key } : {}
|
||||||
|
api_call { http.delete("#{API_URL}#{endpoint}", header: headers) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def api_call
|
def api_call
|
||||||
|
|||||||
@@ -2,6 +2,21 @@
|
|||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Config
|
class Config
|
||||||
attr_accessor :shop_id, :api_key
|
# Source: https://yookassa.ru/developers/using-api/webhooks#ip
|
||||||
|
DEFAULT_WEBHOOK_ALLOWED_IPS = [
|
||||||
|
"185.71.76.0/27",
|
||||||
|
"185.71.77.0/27",
|
||||||
|
"77.75.153.0/25",
|
||||||
|
"77.75.156.11",
|
||||||
|
"77.75.156.35",
|
||||||
|
"77.75.154.128/25",
|
||||||
|
"2a02:5180::/32"
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
attr_accessor :shop_id, :api_key, :webhook_token, :webhook_allowed_ips
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@webhook_allowed_ips = DEFAULT_WEBHOOK_ALLOWED_IPS.dup
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/deal"
|
require_relative "entity/deal"
|
||||||
require_relative "./entity/collection"
|
require_relative "entity/collection"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Deals < Client
|
class Deals < Client
|
||||||
@@ -13,7 +13,7 @@ module Yookassa
|
|||||||
|
|
||||||
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("deals", payload: payload, idempotency_key: idempotency_key)
|
data = post("deals", payload: payload, idempotency_key: idempotency_key)
|
||||||
Entity::Deal.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Deal.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list
|
def list
|
||||||
|
|||||||
7
lib/yookassa/engine.rb
Обычный файл
7
lib/yookassa/engine.rb
Обычный файл
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Yookassa
|
||||||
|
class Engine < ::Rails::Engine
|
||||||
|
isolate_namespace Yookassa
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./payment"
|
require_relative "payment"
|
||||||
require_relative "./receipt"
|
require_relative "receipt"
|
||||||
require_relative "./refund"
|
require_relative "refund"
|
||||||
require_relative "./webhook"
|
require_relative "webhook"
|
||||||
require_relative "./deal"
|
require_relative "deal"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
require_relative "./payment_methods"
|
require_relative "payment_methods"
|
||||||
require_relative "./confirmation"
|
require_relative "confirmation"
|
||||||
require_relative "./recipient"
|
require_relative "recipient"
|
||||||
require_relative "./cancellation_details"
|
require_relative "cancellation_details"
|
||||||
require_relative "./authorization_details"
|
require_relative "authorization_details"
|
||||||
require_relative "./transfer"
|
require_relative "transfer"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./card"
|
require_relative "card"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./customer"
|
require_relative "customer"
|
||||||
require_relative "./product"
|
require_relative "product"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
require_relative "./payout_destinations"
|
require_relative "payout_destinations"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./card"
|
require_relative "card"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
require_relative "./supplier"
|
require_relative "supplier"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./customer"
|
require_relative "customer"
|
||||||
require_relative "./product"
|
require_relative "product"
|
||||||
require_relative "./settlement"
|
require_relative "settlement"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
require_relative "./source"
|
require_relative "source"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
require_relative "./amount"
|
require_relative "amount"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./types"
|
require_relative "types"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
module Entity
|
module Entity
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/payment"
|
require_relative "entity/payment"
|
||||||
require_relative "./entity/collection"
|
require_relative "entity/collection"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Payments < Client
|
class Payments < Client
|
||||||
@@ -13,17 +13,17 @@ module Yookassa
|
|||||||
|
|
||||||
def create(payment:, idempotency_key: SecureRandom.hex(10))
|
def create(payment:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("payments", payload: payment, idempotency_key: idempotency_key)
|
data = post("payments", payload: payment, idempotency_key: idempotency_key)
|
||||||
Entity::Payment.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Payment.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def capture(payment_id:, idempotency_key: SecureRandom.hex(10))
|
def capture(payment_id:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("payments/#{payment_id}/capture", idempotency_key: idempotency_key)
|
data = post("payments/#{payment_id}/capture", idempotency_key: idempotency_key)
|
||||||
Entity::Payment.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Payment.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cancel(payment_id:, idempotency_key: SecureRandom.hex(10))
|
def cancel(payment_id:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("payments/#{payment_id}/cancel", idempotency_key: idempotency_key)
|
data = post("payments/#{payment_id}/cancel", idempotency_key: idempotency_key)
|
||||||
Entity::Payment.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Payment.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list(filters: {})
|
def list(filters: {})
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/payout"
|
require_relative "entity/payout"
|
||||||
require_relative "./entity/collection"
|
require_relative "entity/collection"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Payouts < Client
|
class Payouts < Client
|
||||||
@@ -13,7 +13,7 @@ module Yookassa
|
|||||||
|
|
||||||
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("payouts", payload: payload, idempotency_key: idempotency_key)
|
data = post("payouts", payload: payload, idempotency_key: idempotency_key)
|
||||||
Entity::Payout.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Payout.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
8
lib/yookassa/railtie.rb
Обычный файл
8
lib/yookassa/railtie.rb
Обычный файл
@@ -0,0 +1,8 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "yookassa/engine"
|
||||||
|
|
||||||
|
module Yookassa
|
||||||
|
class Railtie < ::Rails::Railtie
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/receipt"
|
require_relative "entity/receipt"
|
||||||
require_relative "./entity/collection"
|
require_relative "entity/collection"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Receipts < Client
|
class Receipts < Client
|
||||||
@@ -13,7 +13,7 @@ module Yookassa
|
|||||||
|
|
||||||
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("receipts", payload: payload, idempotency_key: idempotency_key)
|
data = post("receipts", payload: payload, idempotency_key: idempotency_key)
|
||||||
Entity::Receipt.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Receipt.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list(filters: {})
|
def list(filters: {})
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/refund"
|
require_relative "entity/refund"
|
||||||
require_relative "./entity/collection"
|
require_relative "entity/collection"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Refunds < Client
|
class Refunds < Client
|
||||||
@@ -13,7 +13,7 @@ module Yookassa
|
|||||||
|
|
||||||
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("refunds", payload: payload, idempotency_key: idempotency_key)
|
data = post("refunds", payload: payload, idempotency_key: idempotency_key)
|
||||||
Entity::Refund.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Refund.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list(filters: {})
|
def list(filters: {})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/store_info"
|
require_relative "entity/store_info"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Stores < Client
|
class Stores < Client
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
VERSION = "0.2.0"
|
VERSION = "0.2.1"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "./client"
|
require_relative "client"
|
||||||
require_relative "./entity/webhook"
|
require_relative "entity/webhook"
|
||||||
require_relative "./entity/collection"
|
require_relative "entity/collection"
|
||||||
|
|
||||||
module Yookassa
|
module Yookassa
|
||||||
class Webhooks < Client
|
class Webhooks < Client
|
||||||
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
def create(payload:, idempotency_key: SecureRandom.hex(10))
|
||||||
data = post("webhooks", payload: payload, idempotency_key: idempotency_key)
|
data = post("webhooks", payload: payload, idempotency_key: idempotency_key)
|
||||||
Entity::Webhook.new(**data.merge(idempotency_key: idempotency_key))
|
Entity::Webhook.new(**data, idempotency_key: idempotency_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list
|
def list
|
||||||
@@ -17,7 +17,7 @@ module Yookassa
|
|||||||
end
|
end
|
||||||
|
|
||||||
def delete(webhook_id:)
|
def delete(webhook_id:)
|
||||||
delete("webhooks/#{webhook_id}")
|
super("webhooks/#{webhook_id}")
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,9 +8,16 @@ end
|
|||||||
|
|
||||||
require "yookassa"
|
require "yookassa"
|
||||||
require "webmock/rspec"
|
require "webmock/rspec"
|
||||||
|
WebMock.disable_net_connect!(allow_localhost: true)
|
||||||
|
|
||||||
|
Dir[File.expand_path("support/**/*.rb", __dir__)].each { |file| require file }
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.order = :random
|
config.order = :random
|
||||||
config.filter_run focus: true
|
config.filter_run focus: true
|
||||||
config.run_all_when_everything_filtered = true
|
config.run_all_when_everything_filtered = true
|
||||||
|
|
||||||
|
config.before do
|
||||||
|
WebMock.reset!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
130
spec/support/rails_test_app.rb
Обычный файл
130
spec/support/rails_test_app.rb
Обычный файл
@@ -0,0 +1,130 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
begin
|
||||||
|
require "logger"
|
||||||
|
require "rack/test"
|
||||||
|
require "rails"
|
||||||
|
require "action_controller/railtie"
|
||||||
|
require "capybara"
|
||||||
|
require "capybara/rspec"
|
||||||
|
require "capybara/cuprite"
|
||||||
|
rescue LoadError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if defined?(Rails)
|
||||||
|
require "yookassa/engine"
|
||||||
|
require File.expand_path("../../app/controllers/yookassa/webhooks_controller", __dir__)
|
||||||
|
|
||||||
|
class YookassaSpecBrowserController < ActionController::Base
|
||||||
|
def index # rubocop:disable Metrics/MethodLength
|
||||||
|
render inline: <<~HTML
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<button id="send">Send webhook</button>
|
||||||
|
<div id="result">pending</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById("send").addEventListener("click", function() {
|
||||||
|
fetch("/yookassa/webhooks/<%= Yookassa.config.webhook_token %>", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
event: "payment.succeeded",
|
||||||
|
object: { id: "browser-payment", status: "succeeded" }
|
||||||
|
})
|
||||||
|
}).then(function(response) {
|
||||||
|
document.getElementById("result").textContent = String(response.status);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class YookassaSpecCustomWebhooksController < Yookassa::WebhooksController
|
||||||
|
class_attribute :last_payload, default: nil
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_webhook(payload)
|
||||||
|
self.class.last_payload = payload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class YookassaSpecApp < Rails::Application
|
||||||
|
config.root = File.expand_path("../..", __dir__)
|
||||||
|
config.eager_load = false
|
||||||
|
config.secret_key_base = "test-secret-key-base"
|
||||||
|
config.logger = Logger.new(nil)
|
||||||
|
config.hosts << "www.example.com"
|
||||||
|
config.hosts << "example.org"
|
||||||
|
config.hosts << "localhost"
|
||||||
|
config.hosts << "127.0.0.1"
|
||||||
|
config.consider_all_requests_local = true
|
||||||
|
end
|
||||||
|
|
||||||
|
YookassaSpecApp.initialize!
|
||||||
|
|
||||||
|
YookassaSpecApp.routes.draw do
|
||||||
|
mount Yookassa::Engine => "/yookassa"
|
||||||
|
post "/custom-yookassa/:token", to: "yookassa_spec_custom_webhooks#create"
|
||||||
|
get "/browser", to: "yookassa_spec_browser#index"
|
||||||
|
end
|
||||||
|
|
||||||
|
Capybara.app = YookassaSpecApp
|
||||||
|
Capybara.server = :webrick
|
||||||
|
Capybara.default_max_wait_time = 5
|
||||||
|
Capybara.register_driver(:cuprite) do |app|
|
||||||
|
browser_options = {
|
||||||
|
"no-sandbox" => nil,
|
||||||
|
"disable-dev-shm-usage" => nil,
|
||||||
|
"disable-gpu" => nil,
|
||||||
|
"disable-software-rasterizer" => nil,
|
||||||
|
"disable-setuid-sandbox" => nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ENV["CI"]
|
||||||
|
browser_options.merge!(
|
||||||
|
"disable-background-timer-throttling" => nil,
|
||||||
|
"disable-backgrounding-occluded-windows" => nil,
|
||||||
|
"disable-renderer-backgrounding" => nil,
|
||||||
|
"disable-ipc-flooding-protection" => nil
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
options = {
|
||||||
|
window_size: [1280, 1024],
|
||||||
|
browser_options: browser_options,
|
||||||
|
timeout: ENV.fetch("FERRUM_TIMEOUT", 30).to_i,
|
||||||
|
headless: true,
|
||||||
|
process_timeout: ENV.fetch("FERRUM_PROCESS_TIMEOUT", ENV["CI"] ? 120 : 30).to_i
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome_path = ENV.fetch("CHROME_PATH", nil)
|
||||||
|
options[:browser_path] = chrome_path if chrome_path
|
||||||
|
|
||||||
|
Capybara::Cuprite::Driver.new(app, **options)
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include Rack::Test::Methods
|
||||||
|
|
||||||
|
config.define_derived_metadata(file_path: %r{/spec/yookassa/webhooks_}) do |metadata|
|
||||||
|
metadata[:rails] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
config.before(:each, rails: true) do
|
||||||
|
Yookassa.configure do |yookassa_config|
|
||||||
|
yookassa_config.shop_id = "shop-id"
|
||||||
|
yookassa_config.api_key = "api-key"
|
||||||
|
yookassa_config.webhook_token = "secret-token"
|
||||||
|
yookassa_config.webhook_allowed_ips = Yookassa::Config::DEFAULT_WEBHOOK_ALLOWED_IPS.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
YookassaSpecCustomWebhooksController.last_payload = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
29
spec/yookassa/webhooks_browser_spec.rb
Обычный файл
29
spec/yookassa/webhooks_browser_spec.rb
Обычный файл
@@ -0,0 +1,29 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe "Yookassa webhook endpoint", :rails, :browser do
|
||||||
|
include Capybara::DSL
|
||||||
|
|
||||||
|
around do |example|
|
||||||
|
previous_driver = Capybara.current_driver
|
||||||
|
Capybara.current_driver = :cuprite
|
||||||
|
example.run
|
||||||
|
Capybara.current_driver = previous_driver
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
Yookassa.config.webhook_allowed_ips = ["127.0.0.1"]
|
||||||
|
|
||||||
|
payments_client = instance_double(Yookassa::Payments)
|
||||||
|
allow(Yookassa).to receive(:payments).and_return(payments_client)
|
||||||
|
allow(payments_client).to receive(:find).with(payment_id: "browser-payment")
|
||||||
|
.and_return(instance_double(Yookassa::Entity::Payment, id: "browser-payment", status: "succeeded"))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "accepts browser-submitted webhook request" do
|
||||||
|
visit "/browser"
|
||||||
|
|
||||||
|
click_button "Send webhook"
|
||||||
|
|
||||||
|
expect(page).to have_text("200")
|
||||||
|
end
|
||||||
|
end
|
||||||
113
spec/yookassa/webhooks_controller_spec.rb
Обычный файл
113
spec/yookassa/webhooks_controller_spec.rb
Обычный файл
@@ -0,0 +1,113 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe Yookassa::WebhooksController do
|
||||||
|
def app
|
||||||
|
YookassaSpecApp
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:payload_hash) do
|
||||||
|
{
|
||||||
|
"event" => "payment.succeeded",
|
||||||
|
"object" => {
|
||||||
|
"id" => "payment-1",
|
||||||
|
"status" => "succeeded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:payload) { JSON.generate(payload_hash) }
|
||||||
|
let(:headers) do
|
||||||
|
{
|
||||||
|
"CONTENT_TYPE" => "application/json",
|
||||||
|
"REMOTE_ADDR" => remote_addr
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:remote_addr) { "185.71.76.1" }
|
||||||
|
|
||||||
|
describe "POST /yookassa/webhooks/:token", :rails do
|
||||||
|
it "accepts webhook when token, remote_ip, and API re-fetch checks pass" do
|
||||||
|
payments_client = instance_double(Yookassa::Payments)
|
||||||
|
allow(Yookassa).to receive(:payments).and_return(payments_client)
|
||||||
|
allow(payments_client).to receive(:find).with(payment_id: "payment-1")
|
||||||
|
.and_return(instance_double(Yookassa::Entity::Payment, id: "payment-1", status: "succeeded"))
|
||||||
|
|
||||||
|
post "/yookassa/webhooks/secret-token", payload, headers
|
||||||
|
|
||||||
|
expect(last_response.status).to eq(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "rejects webhook when token is invalid" do
|
||||||
|
payments_client = instance_double(Yookassa::Payments)
|
||||||
|
allow(Yookassa).to receive(:payments).and_return(payments_client)
|
||||||
|
allow(payments_client).to receive(:find).with(payment_id: "payment-1")
|
||||||
|
.and_return(instance_double(Yookassa::Entity::Payment, id: "payment-1", status: "succeeded"))
|
||||||
|
|
||||||
|
post "/yookassa/webhooks/wrong-token", payload, headers
|
||||||
|
|
||||||
|
expect(last_response.status).to eq(401)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "rejects webhook when source remote_ip is outside allowlist" do
|
||||||
|
payments_client = instance_double(Yookassa::Payments)
|
||||||
|
allow(Yookassa).to receive(:payments).and_return(payments_client)
|
||||||
|
allow(payments_client).to receive(:find).with(payment_id: "payment-1")
|
||||||
|
.and_return(instance_double(Yookassa::Entity::Payment, id: "payment-1", status: "succeeded"))
|
||||||
|
|
||||||
|
post "/yookassa/webhooks/secret-token", payload, headers.merge("REMOTE_ADDR" => "203.0.113.10")
|
||||||
|
|
||||||
|
expect(last_response.status).to eq(401)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "rejects webhook when fetched object differs by status" do
|
||||||
|
payments_client = instance_double(Yookassa::Payments)
|
||||||
|
allow(Yookassa).to receive(:payments).and_return(payments_client)
|
||||||
|
allow(payments_client).to receive(:find).with(payment_id: "payment-1")
|
||||||
|
.and_return(instance_double(Yookassa::Entity::Payment, id: "payment-1", status: "pending"))
|
||||||
|
|
||||||
|
post "/yookassa/webhooks/secret-token", payload, headers
|
||||||
|
|
||||||
|
expect(last_response.status).to eq(401)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "logging", :rails do
|
||||||
|
it "logs auth result when webhook authentication fails" do
|
||||||
|
controller = described_class.new
|
||||||
|
|
||||||
|
allow(controller).to receive(:token_valid?).and_return(true)
|
||||||
|
allow(controller).to receive(:source_ip_allowed?).and_return(false)
|
||||||
|
allow(controller).to receive(:payload_matches_api_object?).and_return(true)
|
||||||
|
allow(controller).to receive(:request).and_return(instance_double(ActionDispatch::Request, remote_ip: "192.168.0.10"))
|
||||||
|
|
||||||
|
expect(Rails.logger).to receive(:info).with(include("Auth failed: token=true, ip=false, api=true"))
|
||||||
|
|
||||||
|
expect(controller.send(:authentic_webhook?, {})).to eq(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "IP source check", :rails do
|
||||||
|
it "uses request.remote_ip" do
|
||||||
|
controller = described_class.new
|
||||||
|
request = instance_double(ActionDispatch::Request)
|
||||||
|
|
||||||
|
allow(request).to receive(:remote_ip).and_return("185.71.76.5")
|
||||||
|
allow(request).to receive(:ip).and_raise("request.ip should not be used")
|
||||||
|
allow(controller).to receive(:request).and_return(request)
|
||||||
|
|
||||||
|
expect(controller.send(:source_ip_allowed?)).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "controller inheritance", :rails do
|
||||||
|
it "allows app controller override via inheritance" do
|
||||||
|
payments_client = instance_double(Yookassa::Payments)
|
||||||
|
allow(Yookassa).to receive(:payments).and_return(payments_client)
|
||||||
|
allow(payments_client).to receive(:find).with(payment_id: "payment-1")
|
||||||
|
.and_return(instance_double(Yookassa::Entity::Payment, id: "payment-1", status: "succeeded"))
|
||||||
|
|
||||||
|
post "/custom-yookassa/secret-token", payload, headers
|
||||||
|
|
||||||
|
expect(last_response.status).to eq(200)
|
||||||
|
expect(YookassaSpecCustomWebhooksController.last_payload).to eq(payload_hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -15,14 +15,14 @@ Gem::Specification.new do |spec|
|
|||||||
spec.license = "MIT"
|
spec.license = "MIT"
|
||||||
|
|
||||||
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
||||||
spec.test_files = spec.files.grep(/^spec/)
|
|
||||||
spec.extra_rdoc_files = Dir["README.md", "LICENSE", "CHANGELOG.md"]
|
spec.extra_rdoc_files = Dir["README.md", "LICENSE", "CHANGELOG.md"]
|
||||||
|
|
||||||
spec.require_paths = ["lib"]
|
spec.require_paths = ["lib"]
|
||||||
|
|
||||||
spec.required_ruby_version = ">= 3.3"
|
spec.required_ruby_version = ">= 3.3"
|
||||||
|
|
||||||
spec.add_runtime_dependency "dry-struct"
|
spec.add_dependency "dry-struct"
|
||||||
spec.add_runtime_dependency "dry-validation"
|
spec.add_dependency "dry-validation"
|
||||||
spec.add_runtime_dependency "rs-httpclient", "= 3.0.0.beta1"
|
spec.add_dependency "rs-httpclient", "= 3.0.0.beta1"
|
||||||
|
spec.metadata["rubygems_mfa_required"] = "true"
|
||||||
end
|
end
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user