Showing preview only (238K chars total). Download the full file or copy to clipboard to get everything.
Repository: thefrontside/stripe-rails
Branch: master
Commit: 2e21a48c3aac
Files: 109
Total size: 212.1 KB
Directory structure:
gitextract_b2ydg9ox/
├── .codeclimate.yml
├── .editorconfig
├── .github/
│ ├── issue_template.md
│ ├── pull_request_template.md
│ └── workflows/
│ └── ruby.yml
├── .gitignore
├── .rubocop.yml
├── CODE_OF_CONDUCT.md
├── Changelog.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── app/
│ ├── assets/
│ │ └── stripe/
│ │ ├── stripe_elements.css
│ │ └── stripe_elements.js
│ ├── controllers/
│ │ └── stripe/
│ │ ├── application_controller.rb
│ │ └── events_controller.rb
│ ├── helpers/
│ │ └── stripe/
│ │ └── javascript_helper.rb
│ ├── models/
│ │ └── stripe/
│ │ └── event_dispatch.rb
│ └── views/
│ └── stripe/
│ ├── _elements.html.erb
│ ├── _elements_js.html.erb
│ └── _js.html.erb
├── config/
│ ├── locales/
│ │ └── en.yml
│ └── routes.rb
├── gemfiles/
│ ├── gemfiles/
│ │ └── rails70.gemfile
│ ├── rails60.gemfile
│ ├── rails61.gemfile
│ ├── rails70.gemfile
│ └── rails71.gemfile
├── lib/
│ ├── generators/
│ │ ├── stripe/
│ │ │ └── install_generator.rb
│ │ └── templates/
│ │ ├── coupons.rb
│ │ ├── plans.rb
│ │ ├── prices.rb
│ │ └── products.rb
│ ├── stripe/
│ │ ├── billing_tier.rb
│ │ ├── callbacks/
│ │ │ └── builder.rb
│ │ ├── callbacks.rb
│ │ ├── configuration_builder.rb
│ │ ├── coupons.rb
│ │ ├── current_api_version.rb
│ │ ├── engine.rb
│ │ ├── plans.rb
│ │ ├── prices.rb
│ │ ├── products.rb
│ │ ├── rails/
│ │ │ ├── tasks.rake
│ │ │ ├── testing.rb
│ │ │ └── version.rb
│ │ └── rails.rb
│ └── stripe-rails.rb
├── stripe-rails.gemspec
└── test/
├── callbacks_spec.rb
├── coupon_builder_spec.rb
├── dummy/
│ ├── README.rdoc
│ ├── Rakefile
│ ├── app/
│ │ ├── assets/
│ │ │ ├── javascripts/
│ │ │ │ └── application.js
│ │ │ └── stylesheets/
│ │ │ └── application.css
│ │ ├── controllers/
│ │ │ ├── apis_controller.rb
│ │ │ ├── application_controller.rb
│ │ │ └── stripes_controller.rb
│ │ ├── helpers/
│ │ │ └── application_helper.rb
│ │ ├── mailers/
│ │ │ └── .gitkeep
│ │ ├── models/
│ │ │ ├── .gitkeep
│ │ │ └── dummy/
│ │ │ └── model_with_callbacks.rb
│ │ └── views/
│ │ ├── layouts/
│ │ │ └── application.html.erb
│ │ └── stripes/
│ │ └── new.html.erb
│ ├── config/
│ │ ├── application.rb
│ │ ├── boot.rb
│ │ ├── database.yml
│ │ ├── environment.rb
│ │ ├── environments/
│ │ │ ├── development.rb
│ │ │ ├── production.rb
│ │ │ └── test.rb
│ │ ├── initializers/
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── inflections.rb
│ │ │ ├── mime_types.rb
│ │ │ ├── secret_token.rb
│ │ │ ├── session_store.rb
│ │ │ └── wrap_parameters.rb
│ │ ├── locales/
│ │ │ └── en.yml
│ │ ├── routes.rb
│ │ └── stripe/
│ │ ├── plans.rb
│ │ └── prices.rb
│ ├── config.ru
│ ├── lib/
│ │ ├── assets/
│ │ │ └── .gitkeep
│ │ └── dummy/
│ │ └── module_with_callbacks.rb
│ ├── log/
│ │ └── .gitkeep
│ ├── public/
│ │ ├── 404.html
│ │ ├── 422.html
│ │ └── 500.html
│ └── script/
│ └── rails
├── dummy_apis_controller_spec.rb
├── dummy_stripes_controller_spec.rb
├── event.json
├── events_controller_spec.rb
├── fixtures/
│ ├── stripe_plans.json
│ ├── stripe_plans_headers.json
│ ├── stripe_plans_headers_2017.json
│ └── stripe_prices.json
├── invoice.json
├── javascript_helper_spec.rb
├── plan_builder_spec.rb
├── price_builder_spec.rb
├── product_builder_spec.rb
├── spec_helper.rb
├── stripe_initializers_spec.rb
├── support/
│ ├── application_system_test_case.rb
│ ├── callback_helpers.rb
│ └── fixture_loader.rb
└── testing_spec.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .codeclimate.yml
================================================
---
engines:
brakeman:
enabled: true
duplication:
enabled: true
config:
languages:
- ruby
fixme:
enabled: true
rubocop:
enabled: true
ratings:
paths:
- Gemfile.lock
- "**.erb"
- "**.haml"
- "**.rb"
- "**.rhtml"
- "**.slim"
- "**.css"
- "**.inc"
- "**.js"
- "**.jsx"
- "**.module"
exclude_paths:
- config/
- test/
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
[*.rb]
indent_style = space
indent_size = 2
charset = utf-8
================================================
FILE: .github/issue_template.md
================================================
<!--
Please give us ~4 weeks to get back to you.
-->
================================================
FILE: .github/pull_request_template.md
================================================
<!--
Thanks a bunch for helping out with the project!
Please remember to,
1. Add tests if they do not exist, fix em if they are breaking
2. Fix any issues that are stopping Code Climate from passing
2. Add a short description of the feature and tag yourself on Changelog.md
That's it!
Please give me ~4 week to get back to you.
-->
================================================
FILE: .github/workflows/ruby.yml
================================================
name: Ruby
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
ruby: [3.0.7, 3.1.6, 3.2.5, 3.3.4]
gemfile: [gemfiles/rails71.gemfile, gemfiles/rails70.gemfile, gemfiles/rails61.gemfile, gemfiles/rails60.gemfile] # Gemfile fails on test/callbacks_spec.rb:52
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Set up Code Climate
run: |
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
./cc-test-reporter before-build
- name: Build and Test
env:
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
RUBY_VERSION: ${{ matrix.ruby }}
run: |
bundle install --jobs 4 --retry 3
bundle exec rake
if [ `basename $BUNDLE_GEMFILE` == "Gemfile" ] && [ $RUBY_VERSION == "3.3.4" ] && [ ! -z ${CC_TEST_REPORTER_ID} ] ;
then ./cc-test-reporter after-build --exit-code $? ;
fi
================================================
FILE: .gitignore
================================================
*.gem
gemfiles/*.lock
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
test/dummy/**/*.log
tmp
.rvmrc
.DS_Store
.ruby-version
================================================
FILE: .rubocop.yml
================================================
AllCops:
DisabledByDefault: true
#################### Lint ################################
Lint/AmbiguousOperator:
Description: >-
Checks for ambiguous operators in the first argument of a
method invocation without parentheses.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args'
Enabled: true
Lint/AmbiguousRegexpLiteral:
Description: >-
Checks for ambiguous regexp literals in the first argument of
a method invocation without parenthesis.
Enabled: true
Lint/AssignmentInCondition:
Description: "Don't use assignment in conditions."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition'
Enabled: true
Layout/BlockAlignment:
Description: 'Align block ends correctly.'
Enabled: true
Lint/CircularArgumentReference:
Description: "Don't refer to the keyword argument in the default value."
Enabled: true
Layout/ConditionPosition:
Description: >-
Checks for condition placed in a confusing position relative to
the keyword.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition'
Enabled: true
Lint/Debugger:
Description: 'Check for debugger calls.'
Enabled: true
Layout/DefEndAlignment:
Description: 'Align ends corresponding to defs correctly.'
Enabled: true
Lint/DeprecatedClassMethods:
Description: 'Check for deprecated class method calls.'
Enabled: true
Lint/DuplicateMethods:
Description: 'Check for duplicate methods calls.'
Enabled: true
Lint/EachWithObjectArgument:
Description: 'Check for immutable argument given to each_with_object.'
Enabled: true
Lint/ElseLayout:
Description: 'Check for odd code arrangement in an else block.'
Enabled: true
Lint/EmptyEnsure:
Description: 'Checks for empty ensure block.'
Enabled: true
Lint/EmptyInterpolation:
Description: 'Checks for empty string interpolation.'
Enabled: true
Layout/EndAlignment:
Description: 'Align ends correctly.'
Enabled: true
Lint/EndInMethod:
Description: 'END blocks should not be placed inside method definitions.'
Enabled: true
Lint/EnsureReturn:
Description: 'Do not use return in an ensure block.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure'
Enabled: true
Security/Eval:
Description: 'The use of eval represents a serious security risk.'
Enabled: true
Lint/FormatParameterMismatch:
Description: 'The number of parameters to format/sprint must match the fields.'
Enabled: true
Lint/HandleExceptions:
Description: "Don't suppress exception."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions'
Enabled: true
Lint/LiteralAsCondition:
Description: 'Checks of literals used in conditions.'
Enabled: true
Lint/LiteralInInterpolation:
Description: 'Checks for literals used in interpolation.'
Enabled: true
Lint/Loop:
Description: >-
Use Kernel#loop with break rather than begin/end/until or
begin/end/while for post-loop tests.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break'
Enabled: true
Lint/NestedMethodDefinition:
Description: 'Do not use nested method definitions.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods'
Enabled: true
Lint/NonLocalExitFromIterator:
Description: 'Do not use return in iterator to cause non-local exit.'
Enabled: true
Lint/ParenthesesAsGroupedExpression:
Description: >-
Checks for method calls with a space before the opening
parenthesis.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
Enabled: true
Lint/RequireParentheses:
Description: >-
Use parentheses in the method call to avoid confusion
about precedence.
Enabled: true
Lint/RescueException:
Description: 'Avoid rescuing the Exception class.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
Enabled: true
Lint/ShadowingOuterLocalVariable:
Description: >-
Do not use the same name as outer local variable
for block arguments or block local variables.
Enabled: true
Lint/StringConversionInInterpolation:
Description: 'Checks for Object#to_s usage in string interpolation.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s'
Enabled: true
Lint/UnderscorePrefixedVariableName:
Description: 'Do not use prefix `_` for a variable that is used.'
Enabled: true
Lint/UnneededCopDisableDirective:
Description: >-
Checks for rubocop:disable comments that can be removed.
Note: this cop is not disabled when disabling all cops.
It must be explicitly disabled.
Enabled: true
Lint/UnusedBlockArgument:
Description: 'Checks for unused block arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: true
Lint/UnusedMethodArgument:
Description: 'Checks for unused method arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: true
Lint/UnreachableCode:
Description: 'Unreachable code.'
Enabled: true
Lint/UselessAccessModifier:
Description: 'Checks for useless access modifiers.'
Enabled: true
Lint/UselessAssignment:
Description: 'Checks for useless assignment to a local variable.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: true
Lint/UselessComparison:
Description: 'Checks for comparison of something with itself.'
Enabled: true
Lint/UselessElseWithoutRescue:
Description: 'Checks for useless `else` in `begin..end` without `rescue`.'
Enabled: true
Lint/UselessSetterCall:
Description: 'Checks for useless setter call to a local variable.'
Enabled: true
Lint/Void:
Description: 'Possible use of operator/literal/variable in void context.'
Enabled: true
###################### Metrics ####################################
Metrics/AbcSize:
Description: >-
A calculated magnitude based on number of assignments,
branches, and conditions.
Reference: 'http://c2.com/cgi/wiki?AbcMetric'
Enabled: false
Max: 20
Metrics/BlockNesting:
Description: 'Avoid excessive block nesting'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count'
Enabled: true
Max: 4
Metrics/ClassLength:
Description: 'Avoid classes longer than 250 lines of code.'
Enabled: true
Max: 250
Metrics/CyclomaticComplexity:
Description: >-
A complexity metric that is strongly correlated to the number
of test cases needed to validate a method.
Enabled: true
Metrics/LineLength:
Description: 'Limit lines to 80 characters.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits'
Enabled: false
Metrics/MethodLength:
Description: 'Avoid methods longer than 30 lines of code.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods'
Enabled: true
Max: 30
Metrics/ModuleLength:
Description: 'Avoid modules longer than 250 lines of code.'
Enabled: true
Max: 250
Metrics/ParameterLists:
Description: 'Avoid parameter lists longer than three or four parameters.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params'
Enabled: true
Metrics/PerceivedComplexity:
Description: >-
A complexity metric geared towards measuring complexity for a
human reader.
Enabled: false
##################### Performance #############################
Performance/Count:
Description: >-
Use `count` instead of `select...size`, `reject...size`,
`select...count`, `reject...count`, `select...length`,
and `reject...length`.
Enabled: true
Performance/Detect:
Description: >-
Use `detect` instead of `select.first`, `find_all.first`,
`select.last`, and `find_all.last`.
Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
Enabled: true
Performance/FlatMap:
Description: >-
Use `Enumerable#flat_map`
instead of `Enumerable#map...Array#flatten(1)`
or `Enumberable#collect..Array#flatten(1)`
Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
Enabled: true
EnabledForFlattenWithoutParams: false
# If enabled, this cop will warn about usages of
# `flatten` being called without any parameters.
# This can be dangerous since `flat_map` will only flatten 1 level, and
# `flatten` without any parameters can flatten multiple levels.
Performance/ReverseEach:
Description: 'Use `reverse_each` instead of `reverse.each`.'
Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
Enabled: true
Performance/Sample:
Description: >-
Use `sample` instead of `shuffle.first`,
`shuffle.last`, and `shuffle[Fixnum]`.
Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code'
Enabled: true
Performance/Size:
Description: >-
Use `size` instead of `count` for counting
the number of elements in `Array` and `Hash`.
Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code'
Enabled: true
Performance/StringReplacement:
Description: >-
Use `tr` instead of `gsub` when you are replacing the same
number of characters. Use `delete` instead of `gsub` when
you are deleting characters.
Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code'
Enabled: true
##################### Rails ##################################
Rails/ActionFilter:
Description: 'Enforces consistent use of action filter methods.'
Enabled: false
Rails/Date:
Description: >-
Checks the correct usage of date aware methods,
such as Date.today, Date.current etc.
Enabled: false
Rails/Delegate:
Description: 'Prefer delegate method for delegations.'
Enabled: false
Rails/FindBy:
Description: 'Prefer find_by over where.first.'
Enabled: false
Rails/FindEach:
Description: 'Prefer all.find_each over all.find.'
Enabled: false
Rails/HasAndBelongsToMany:
Description: 'Prefer has_many :through to has_and_belongs_to_many.'
Enabled: false
Rails/Output:
Description: 'Checks for calls to puts, print, etc.'
Enabled: false
Rails/ReadWriteAttribute:
Description: >-
Checks for read_attribute(:attr) and
write_attribute(:attr, val).
Enabled: false
Rails/ScopeArgs:
Description: 'Checks the arguments of ActiveRecord scopes.'
Enabled: false
Rails/TimeZone:
Description: 'Checks the correct usage of time zone aware methods.'
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time'
Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
Enabled: false
Rails/Validation:
Description: 'Use validates :attribute, hash of validations.'
Enabled: false
################## Style #################################
Layout/AccessModifierIndentation:
Description: Check indentation of private/protected visibility modifiers.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected'
Enabled: false
Naming/AccessorMethodName:
Description: Check the naming of accessor methods for get_/set_.
Enabled: false
Style/Alias:
Description: 'Use alias_method instead of alias.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method'
Enabled: false
Layout/AlignArray:
Description: >-
Align the elements of an array literal if they span more than
one line.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays'
Enabled: false
Layout/AlignHash:
Description: >-
Align the elements of a hash literal if they span more than
one line.
Enabled: false
Layout/AlignParameters:
Description: >-
Align the parameters of a method call if they span more
than one line.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent'
Enabled: false
Style/AndOr:
Description: 'Use &&/|| instead of and/or.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or'
Enabled: false
Style/ArrayJoin:
Description: 'Use Array#join instead of Array#*.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join'
Enabled: false
Style/AsciiComments:
Description: 'Use only ascii symbols in comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments'
Enabled: false
Naming/AsciiIdentifiers:
Description: 'Use only ascii symbols in identifiers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers'
Enabled: false
Style/Attr:
Description: 'Checks for uses of Module#attr.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr'
Enabled: false
Style/BeginBlock:
Description: 'Avoid the use of BEGIN blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks'
Enabled: false
Style/BarePercentLiterals:
Description: 'Checks if usage of %() or %Q() matches configuration.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand'
Enabled: false
Style/BlockComments:
Description: 'Do not use block comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments'
Enabled: false
Layout/BlockEndNewline:
Description: 'Put end statement of multiline block on its own line.'
Enabled: false
Style/BlockDelimiters:
Description: >-
Avoid using {...} for multi-line blocks (multiline chaining is
always ugly).
Prefer {...} over do...end for single-line blocks.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
Enabled: false
Style/BracesAroundHashParameters:
Description: 'Enforce braces style around hash parameters.'
Enabled: false
Style/CaseEquality:
Description: 'Avoid explicit use of the case equality operator(===).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality'
Enabled: false
Layout/CaseIndentation:
Description: 'Indentation of when in a case/when/[else/]end.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case'
Enabled: false
Style/CharacterLiteral:
Description: 'Checks for uses of character literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals'
Enabled: false
Naming/ClassAndModuleCamelCase:
Description: 'Use CamelCase for classes and modules.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes'
Enabled: false
Style/ClassAndModuleChildren:
Description: 'Checks style of children classes and modules.'
Enabled: false
Style/ClassCheck:
Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.'
Enabled: false
Style/ClassMethods:
Description: 'Use self when defining module/class methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-class-methods'
Enabled: false
Style/ClassVars:
Description: 'Avoid the use of class variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars'
Enabled: false
Layout/ClosingParenthesisIndentation:
Description: 'Checks the indentation of hanging closing parentheses.'
Enabled: false
Style/ColonMethodCall:
Description: 'Do not use :: for method call.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons'
Enabled: false
Style/CommandLiteral:
Description: 'Use `` or %x around command literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x'
Enabled: false
Style/CommentAnnotation:
Description: 'Checks formatting of annotation comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords'
Enabled: false
Layout/CommentIndentation:
Description: 'Indentation of comments.'
Enabled: false
Naming/ConstantName:
Description: 'Constants should use SCREAMING_SNAKE_CASE.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case'
Enabled: false
Style/DefWithParentheses:
Description: 'Use def with parentheses when there are arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
Enabled: false
Style/PreferredHashMethods:
Description: 'Checks for use of deprecated Hash methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
Enabled: false
Style/Documentation:
Description: 'Document classes and non-namespace modules.'
Enabled: false
Layout/DotPosition:
Description: 'Checks the position of the dot in multi-line method calls.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains'
Enabled: false
Style/DoubleNegation:
Description: 'Checks for uses of double negation (!!).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang'
Enabled: false
Style/EachWithObject:
Description: 'Prefer `each_with_object` over `inject` or `reduce`.'
Enabled: false
Layout/ElseAlignment:
Description: 'Align elses and elsifs correctly.'
Enabled: false
Style/EmptyElse:
Description: 'Avoid empty else-clauses.'
Enabled: false
Layout/EmptyLineBetweenDefs:
Description: 'Use empty lines between defs.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods'
Enabled: false
Layout/EmptyLines:
Description: "Don't use several empty lines in a row."
Enabled: false
Layout/EmptyLinesAroundAccessModifier:
Description: "Keep blank lines around access modifiers."
Enabled: false
Layout/EmptyLinesAroundBlockBody:
Description: "Keeps track of empty lines around block bodies."
Enabled: false
Layout/EmptyLinesAroundClassBody:
Description: "Keeps track of empty lines around class bodies."
Enabled: false
Layout/EmptyLinesAroundModuleBody:
Description: "Keeps track of empty lines around module bodies."
Enabled: false
Layout/EmptyLinesAroundMethodBody:
Description: "Keeps track of empty lines around method bodies."
Enabled: false
Style/EmptyLiteral:
Description: 'Prefer literals to Array.new/Hash.new/String.new.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash'
Enabled: false
Style/EndBlock:
Description: 'Avoid the use of END blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks'
Enabled: false
Layout/EndOfLine:
Description: 'Use Unix-style line endings.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf'
Enabled: false
Style/EvenOdd:
Description: 'Favor the use of Fixnum#even? && Fixnum#odd?'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false
Layout/ExtraSpacing:
Description: 'Do not use unnecessary spacing.'
Enabled: false
Naming/FileName:
Description: 'Use snake_case for source file names.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files'
Enabled: false
Layout/InitialIndentation:
Description: >-
Checks the indentation of the first non-blank non-comment line in a file.
Enabled: false
Layout/FirstParameterIndentation:
Description: 'Checks the indentation of the first parameter in a method call.'
Enabled: false
Lint/FlipFlop:
Description: 'Checks for flip flops'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops'
Enabled: false
Style/For:
Description: 'Checks use of for or each in multiline loops.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops'
Enabled: false
Style/FormatString:
Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf'
Enabled: false
Style/GlobalVars:
Description: 'Do not introduce global variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars'
Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html'
Enabled: false
Style/GuardClause:
Description: 'Check for conditionals that can be replaced with guard clauses'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals'
Enabled: false
Style/HashSyntax:
Description: >-
Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax
{ :a => 1, :b => 2 }.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals'
Enabled: false
Style/IfUnlessModifier:
Description: >-
Favor modifier if/unless usage when you have a
single-line body.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier'
Enabled: false
Style/IfWithSemicolon:
Description: 'Do not use if x; .... Use the ternary operator instead.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs'
Enabled: false
Layout/IndentationConsistency:
Description: 'Keep indentation straight.'
Enabled: false
Layout/IndentationWidth:
Description: 'Use 2 spaces for indentation.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
Enabled: false
Layout/IndentArray:
Description: >-
Checks the indentation of the first element in an array
literal.
Enabled: false
Layout/IndentHash:
Description: 'Checks the indentation of the first key in a hash literal.'
Enabled: false
Style/InfiniteLoop:
Description: 'Use Kernel#loop for infinite loops.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop'
Enabled: false
Style/Lambda:
Description: 'Use the new lambda literal syntax for single-line blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line'
Enabled: false
Style/LambdaCall:
Description: 'Use lambda.call(...) instead of lambda.(...).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call'
Enabled: false
Layout/LeadingCommentSpace:
Description: 'Comments should start with a space.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space'
Enabled: false
Style/LineEndConcatenation:
Description: >-
Use \ instead of + or << to concatenate two string literals at
line end.
Enabled: false
Style/MethodCallWithoutArgsParentheses:
Description: 'Do not use parentheses for method calls with no arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens'
Enabled: false
Style/MethodDefParentheses:
Description: >-
Checks if the method definitions have or don't have
parentheses.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
Enabled: false
Naming/MethodName:
Description: 'Use the configured style when naming methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars'
Enabled: false
Style/ModuleFunction:
Description: 'Checks for usage of `extend self` in modules.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function'
Enabled: false
Style/MultilineBlockChain:
Description: 'Avoid multi-line chains of blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
Enabled: false
Layout/MultilineBlockLayout:
Description: 'Ensures newlines after multiline block do statements.'
Enabled: false
Style/MultilineIfThen:
Description: 'Do not use then for multi-line if/unless.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then'
Enabled: false
Layout/MultilineOperationIndentation:
Description: >-
Checks indentation of binary operations that span more than
one line.
Enabled: false
Style/MultilineTernaryOperator:
Description: >-
Avoid multi-line ?: (the ternary operator);
use if/unless instead.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary'
Enabled: false
Style/NegatedIf:
Description: >-
Favor unless over if for negative conditions
(or control flow or).
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives'
Enabled: false
Style/NegatedWhile:
Description: 'Favor until over while for negative conditions.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives'
Enabled: false
Style/NestedTernaryOperator:
Description: 'Use one expression per branch in a ternary operator.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary'
Enabled: false
Style/Next:
Description: 'Use `next` to skip iteration instead of a condition at the end.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals'
Enabled: false
Style/NilComparison:
Description: 'Prefer x.nil? to x == nil.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false
Style/NonNilCheck:
Description: 'Checks for redundant nil checks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks'
Enabled: false
Style/Not:
Description: 'Use ! instead of not.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
Enabled: false
Style/NumericLiterals:
Description: >-
Add underscores to large numeric literals to improve their
readability.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics'
Enabled: false
Style/OneLineConditional:
Description: >-
Favor the ternary operator(?:) over
if/then/else/end constructs.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator'
Enabled: false
Naming/BinaryOperatorParameterName:
Description: 'When defining binary operators, name the argument other.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
Enabled: false
Style/OptionalArguments:
Description: >-
Checks for optional arguments that do not appear at the end
of the argument list
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#optional-arguments'
Enabled: false
Style/ParallelAssignment:
Description: >-
Check for simple usages of parallel assignment.
It will only warn when the number of variables
matches on both sides of the assignment.
This also provides performance benefits
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment'
Enabled: false
Style/ParenthesesAroundCondition:
Description: >-
Don't use parentheses around the condition of an
if/unless/while.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if'
Enabled: false
Style/PercentLiteralDelimiters:
Description: 'Use `%`-literal delimiters consistently'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces'
Enabled: false
Style/PercentQLiterals:
Description: 'Checks if uses of %Q/%q match the configured preference.'
Enabled: false
Style/PerlBackrefs:
Description: 'Avoid Perl-style regex back references.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers'
Enabled: false
Naming/PredicateName:
Description: 'Check the names of predicate methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark'
Enabled: false
Style/Proc:
Description: 'Use proc instead of Proc.new.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc'
Enabled: false
Style/RaiseArgs:
Description: 'Checks the arguments passed to raise/fail.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages'
Enabled: false
Style/RedundantBegin:
Description: "Don't use begin blocks when they are not needed."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit'
Enabled: false
Style/RedundantException:
Description: "Checks for an obsolete RuntimeException argument in raise/fail."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror'
Enabled: false
Style/RedundantReturn:
Description: "Don't use return where it's not required."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return'
Enabled: false
Style/RedundantSelf:
Description: "Don't use self where it's not needed."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required'
Enabled: false
Style/RegexpLiteral:
Description: 'Use / or %r around regular expressions.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r'
Enabled: false
Layout/RescueEnsureAlignment:
Description: 'Align rescues and ensures correctly.'
Enabled: false
Style/RescueModifier:
Description: 'Avoid using rescue in its modifier form.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers'
Enabled: false
Style/SelfAssignment:
Description: >-
Checks for places where self-assignment shorthand should have
been used.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment'
Enabled: false
Style/Semicolon:
Description: "Don't use semicolons to terminate expressions."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon'
Enabled: false
Style/SignalException:
Description: 'Checks for proper usage of fail and raise.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method'
Enabled: false
Style/SingleLineBlockParams:
Description: 'Enforces the names of some block params.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks'
Enabled: false
Style/SingleLineMethods:
Description: 'Avoid single-line methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods'
Enabled: false
Layout/SpaceBeforeFirstArg:
Description: >-
Checks that exactly one space is used between a method name
and the first argument for method calls without parentheses.
Enabled: true
Layout/SpaceAfterColon:
Description: 'Use spaces after colons.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Layout/SpaceAfterComma:
Description: 'Use spaces after commas.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Layout/SpaceAroundKeyword:
Description: 'Use spaces around keywords.'
Enabled: false
Layout/SpaceAfterMethodName:
Description: >-
Do not put a space between a method name and the opening
parenthesis in a method definition.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
Enabled: false
Layout/SpaceAfterNot:
Description: Tracks redundant space after the ! operator.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang'
Enabled: false
Layout/SpaceAfterSemicolon:
Description: 'Use spaces after semicolons.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Layout/SpaceBeforeBlockBraces:
Description: >-
Checks that the left block brace has or doesn't have space
before it.
Enabled: false
Layout/SpaceBeforeComma:
Description: 'No spaces before commas.'
Enabled: false
Layout/SpaceBeforeComment:
Description: >-
Checks for missing space between code and a comment on the
same line.
Enabled: false
Layout/SpaceBeforeSemicolon:
Description: 'No spaces before semicolons.'
Enabled: false
Layout/SpaceInsideBlockBraces:
Description: >-
Checks that block braces have or don't have surrounding space.
For blocks taking parameters, checks that the left brace has
or doesn't have trailing space.
Enabled: false
Layout/SpaceAroundBlockParameters:
Description: 'Checks the spacing inside and after block parameters pipes.'
Enabled: false
Layout/SpaceAroundEqualsInParameterDefault:
Description: >-
Checks that the equals signs in parameter default assignments
have or don't have surrounding space depending on
configuration.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals'
Enabled: false
Layout/SpaceAroundOperators:
Description: 'Use a single space around operators.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Layout/SpaceInsideHashLiteralBraces:
Description: "Use spaces inside hash literal braces - or don't."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Layout/SpaceInsideParens:
Description: 'No spaces after ( or before ).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
Enabled: false
Layout/SpaceInsideRangeLiteral:
Description: 'No spaces inside range literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals'
Enabled: false
Layout/SpaceInsideStringInterpolation:
Description: 'Checks for padding/surrounding spaces inside string interpolation.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#string-interpolation'
Enabled: false
Style/SpecialGlobalVars:
Description: 'Avoid Perl-style global variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms'
Enabled: false
Style/StringLiterals:
Description: 'Checks if uses of quotes match the configured preference.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals'
Enabled: false
Style/StringLiteralsInInterpolation:
Description: >-
Checks if uses of quotes inside expressions in interpolated
strings match the configured preference.
Enabled: false
Style/StructInheritance:
Description: 'Checks for inheritance from Struct.new.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new'
Enabled: false
Style/SymbolLiteral:
Description: 'Use plain symbols instead of string symbols when possible.'
Enabled: false
Style/SymbolProc:
Description: 'Use symbols as procs instead of blocks when possible.'
Enabled: false
Layout/Tab:
Description: 'No hard tabs.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
Enabled: false
Layout/TrailingBlankLines:
Description: 'Checks trailing blank lines and final newline.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof'
Enabled: false
Style/TrailingCommaInArguments:
Description: 'Checks for trailing comma in parameter lists.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma'
Enabled: false
Layout/TrailingWhitespace:
Description: 'Avoid trailing whitespace.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace'
Enabled: false
Style/TrivialAccessors:
Description: 'Prefer attr_* methods to trivial readers/writers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family'
Enabled: false
Style/UnlessElse:
Description: >-
Do not use unless with else. Rewrite these with the positive
case first.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless'
Enabled: false
Style/UnneededCapitalW:
Description: 'Checks for %W when interpolation is not needed.'
Enabled: false
Style/UnneededPercentQ:
Description: 'Checks for %q/%Q when single quotes or double quotes would do.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q'
Enabled: false
Style/TrailingUnderscoreVariable:
Description: >-
Checks for the usage of unneeded trailing underscores at the
end of parallel variable assignment.
Enabled: false
Style/VariableInterpolation:
Description: >-
Don't interpolate global, instance and class variables
directly in strings.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate'
Enabled: false
Naming/VariableName:
Description: 'Use the configured style when naming variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars'
Enabled: false
Style/WhenThen:
Description: 'Use when x then ... for one-line cases.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases'
Enabled: false
Style/WhileUntilDo:
Description: 'Checks for redundant do after while or until.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do'
Enabled: false
Style/WhileUntilModifier:
Description: >-
Favor modifier while/until usage when you have a
single-line body.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier'
Enabled: false
Style/WordArray:
Description: 'Use %w or %W for arrays of words.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
Enabled: false
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at code-of-conduct@frontside.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/
================================================
FILE: Changelog.md
================================================
## Next Release
## 2.6.0 (2024-08-12)
- Add the following callbacks (Thanks @4nd2in !):
* `checkout.session.async_payment_failed`
* `checkout.session.async_payment_succeeded`
* `customer.subscription.paused`
* `customer.subscription.pending_update_applied`
* `customer.subscription.pending_update_expired`
* `customer.subscription.resumed`
* `invoice.deleted`
* `invoice.finalization_failed`
* `invoice.overdue`
* `invoice.will_be_due`
- Fix request handling in rack versions >= 3.1 where `Request#[]` is deprecated (#230). Thanks @4nd2in and
@light-flight !
- adds testing for Ruby 3 and Rails 7
- stops testing on Ruby < 3 and Rails 5
## 2.5.0 (2023-03-21)
- Allow `stripe_elements_tag` to accept a block. Thanks @chip !
## 2.4.0 (2023-02-04)
- Add `tax_behavior` attribute to Price. Thanks @szechyjs !
## 2.3.5 (2022-10-01)
- Fix some deprecation warnings in tests. Thanks @smtlaissezfaire !
- Fix `NameError when loading JavascriptHelper`. Thanks @smtlaissezfaire !
## 2.3.4 (2022-05-03)
- Add setup_intent.canceled and setup_intent.requires_action callbacks. Thanks @jamesjason !
## 2.3.3 (2021-12-17)
- Add checkout.session.expired callback. Thanks @danielwellman !
## 2.3.2 (2021-10-15)
- Add Subscription Schedule and Tax Rate Callbacks #213 . Thanks @lesliepoolman !
## 2.3.1 (2021-09-17)
- Add price callbacks. Thanks @StevenElberger !
## 2.3.0 (2021-03-08)
- Adds testing for Rails 6.0
- Add `name` attribute to coupons. Thanks @ZilvinasKucinskas!
## 2.2.1 (2020-12-22)
- Add payment_intent.requires_action callback, thanks @VadimLeader.
## 2.2.0 (2020-12-06)
- Add Prices as a configuration object. Thanks @jamesprior!
## 2.1.0 (2020-10-18)
- Added option to ignore missing API key and don't show any warning. Thanks @ndbroadbent!
- Handle passing nil to signing_secret= and add tests. Thanks @martron!
## 2.0.0 (2020-09-18)
- Everything from on the 2.0.0.pre release
- includes changes from the 1.10.2 release
## 1.10.2 (2020-09-18)
- adds missing callback `invoice.paid`. Thanks @SyborgStudios.
## 2.0.0.pre (2020-05-29)
* [Breaking] Updated to work only with Rails >= 5.1
* [Breaking] It'll only be tested on Ruby 2.7, 2.6 and 2.5.
* [Breaking] Supports the Stripe gem => 3.15.0 (from 2 years ago)
* [Breaking] Removes Stripe::PingsController controller.
## 1.10.1 (2020-05-29)
- adds missing callbacks for `payment_intent`. Thanks @klapperkopp .
## 1.10.0 (2020-03-31)
- Adds support for using multiple tiers in a plan, thanks @cpsoinos
## 1.9.1 (2019-10-28)
- Fixes issue with `rake stripe:verify` thanks @Millariel !
## 1.9.0 (2019-09-01)
- Adds support for multiple signing secrets. Thanks again @jacobcsmith !
## 1.8.2 (2019-08-31)
- adds missing callbacks for `payment_intent`, `payment_method` and `setup_intent`. Thanks @jacobcsmith !
## 1.8.1 (2019-07-26)
* adds callback for invoice.payment_action_required. Thanks @alexagranov .
* fixes when clearing callbacks after unload doesn't play nice with eager_load. Thanks @alexagranov for reporting the problem and coming up with an initial fix for it.
## 1.8.0 (2019-07-25)
* Configure publishable key from ENV. Thanks @cyu .
## 1.7.2 (2019-06-29)
* fixes `require` error after update from Stripe gem. Thanks @dark-panda !
## 1.7.1 (2019-05-24)
* Don't assume sprockets are loaded thanks @manusajith
## 1.7.0 (2019-05-09)
* [New Feature] add support for Plan to use a constant name different from plan id thanks @alexagranov !
* Add checkout.session.completed webhook thanks @Nitrino !
## 1.6.1 (2019-03-04)
* Add new invoice webhooks thanks @noahezekwugo !
## 1.6.0 (2019-01-08)
* New Year New Feature: Easily include Stripe Elements into your project thanks to @garrettqmartin8 !
* Travis is now testing the gem on Ruby 2.6.0
## 1.5.5 (2018-12-16)
* Fixed issue with Rails development and Spring: Clear callbacks before files are reloaded during development and test - thanks @ndbroadbent
## 1.5.4 (2018-11-14)
* Removes test exception from event dispatch
* Travis is now testing the gem on Ruby 2.5.3, 2.4.5, 2.3.8
## 1.5.3 (2018-10-25)
* Add usage_type, aggregate_usage, and billing_scheme - thanks @garrettqmartin8
## 1.5.2 (2018-10-15)
* fixes undefined method `expand_path' for Stripe::File:Class - Thanks to @BitesGit for reporting this.
## 1.5.1 (2018-10-01)
* Allow statement_descriptor to be set on products - Thanks to @jeanmartin
## 1.5.0 (2018-09-27)
* Add Webhook Signature Validation - Thanks to @wkirby
* Include nickname in the payload for plans - Thanks to @jeanmartin
## 1.4.2 (2018-08-06)
* New attributes for Stripe Billing Plans.
## 1.4.1 (2018-08-03)
* Fixes ActionController::UnknownFormat errors - Thanks to @ndbroadbent !
## 1.4.0 (2018-07-30)
* Spanking new products builder for Stripe Billing (#117) - Thanks to @renchap for suggesting this and to @henryaj for reviewing it.
## 1.3.0 (2018-07-23)
* do not create new product when product id is provided (#115) - Thanks to @renchap for reporting this
* updates travis to latest rubies (#112) - Note that after this change we will only run tests on Ruby 2.5, 2.4 and 2.3
## 1.2.2 (2018-04-16)
* adds callback form `customer.source.expiring`. Thanks @Japestrale!
## 1.2.1 (2018-03-22)
* Fixes Stripe API update on 2018-02-05 that breaks the plan builder (thanks to @georgecheng for reporting this!)
## 1.2.0 (2018-01-02)
* Added additional callbacks (thanks @lloydpick & @dja)
## 1.1.2 (2017-10-25)
* Fixes js partial crash if stripe_js_version is not defined
## 1.1.1 (2017-08-31)
* Make stripe-ruby-mock an optional dependency (thanks @gaffneyc)
## 1.1.0 (2017-08-29)
* Adds a testing module for testing callbacks (thanks @Pyo25)
* Fixes loading with ActionController::API (thanks @gaffneyc)
* Fixes `NoMethodError: NoMethodError (undefined method `object' for #ActionController::Parameters` (thanks to a whole bunch of people for reporting this)
## 1.0.2 (2017-08-15)
* Remove authenticity token check (thanks @lewispb)
* Adding timeout options to config (thanks @rgerard)
* Add 'day' as possible plan interval (thanks @vdragsic and @artemave)
## 1.0.1 (2017-08-08)
* Fixes a bug with Stripe JS V3, i.e. `Stripe.setPublishableKey` is no longer a function. Thanks to @kartikluke for reporting this.
## 1.0.0 (2017-07-24 - Breaky McBreakface)
* [BREAKING] Update to latest stripe events (thanks @hopsoft). Note that if you are using the `after_customer_card_created`, `after_customer_card_updated` or `after_customer_card_deleted` callbacks, you MUST update them to `after_customer_source_created`, `after_customer_source_updated` or `after_customer_source_deleted` respectively. You also need to start using [Stripe API Version > 2015-02-18](https://stripe.com/docs/upgrades#2015-02-18) or else the webhook might not work as expected.
* [BREAKING] Updates to the [latest version of Stripe JS](https://github.com/Everapps/stripe-rails/pull/69). If you were using `stripe_javascript_tag` without specifying the version number, note that it will now default to Stripe JS v3. This version is incompatible with the previous default.
* The gem will only be tested on Rails 4 and 5 [from now on](https://github.com/Everapps/stripe-rails/pull/62).
* Gem will henceforth only [be tested](https://github.com/Everapps/stripe-rails/pull/68) on Ruby >= 2.1.9.
* add statement descriptor to plan attributes (thanks @jbender)
* Relax version constraint on the responders gem
## 0.4.1 (2017-06-01)
* Support for api_version header parameter (thanks @kiddrew)
* Relax version constraint on stripe gem (thanks @gaffneyc)
## 0.4.0 (2017-05-24)
* Support alternate versions of stripe js
## 0.3.2 (2017-03-06)
* add `responders` gem as dependency to support `respond_to` method
* fix unit tests with Rails 4.2 and Rails 5.0
## 0.3.1 (2014-08-07)
* add `eager_load` option to load callbacks into classes in non-eager-loaded enviroments
## 0.3.0 (2014-04-29)
* Rename api_key to secret_key
## 0.2.6 (2013-10-17)
* add `auto_mount` option to allow for manually mounting the webhook endpoints
## 0.2.5 (2013-03-18)
* make the default max redemptions 1
* add stripe::coupons::reset! task to redefine all coupons
## 0.2.2 (2013-01-09)
* bugfix allowing creation of coupons without max_redemptions
## 0.2.1 (2012-12-17)
* manage coupons with config/stripe/coupons.rb
## 0.2.0 (2012-12-13)
* out of the box support for webhooks and critical/non-critical event handlers
* add :only guards for which webhooks you respond to-
* move stripe.js out of asset pipeline, and insert it with utility functions
## 0.1.0 (2012-11-14)
* add config/stripe/plans.rb to define and create plans
* use `STRIPE_API_KEY` as default value of `config.stripe.api_key`
* require stripe.js from asset pipeline
* autoconfigure stripe.js with config.stripe.publishable_key.
* add rake stripe:verify to ensure stripe.com authentication is configured properly
## 0.0.1 (2012-10-12)
* basic railtie
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
# Specify your gem's dependencies in stripe-rails.gemspec
gemspec
gem 'rake'
group :development, :test do
gem 'm'
end
group :test do
gem 'mocha'
gem 'simplecov', '< 0.18', require: false
gem 'stripe-ruby-mock'
gem 'webmock'
# System tests
gem 'capybara'
gem 'puma'
gem 'selenium-webdriver', '>= 4'
end
================================================
FILE: LICENSE
================================================
Copyright (c) 2012-2017 The Frontside Software, Inc.
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Stripe::Rails: A Rails Engine for use with [stripe.com](https://stripe.com)
[](https://badge.fury.io/rb/stripe-rails)
[](https://travis-ci.org/tansengming/stripe-rails)
[](https://codeclimate.com/github/tansengming/stripe-rails)
[](https://codeclimate.com/github/tansengming/stripe-rails/coverage)
[](#)
This gem can help your rails application integrate with Stripe in the following ways
* manage stripe configurations in a single place.
* makes stripe.js available from the asset pipeline.
* manage product, prices, plans and coupons from within your app.
* painlessly receive and validate webhooks from stripe.
[Professionally supported stripe-rails is coming soon](https://tidelift.com/subscription/pkg/rubygems-stripe-rails?utm_source=rubygems-stripe-rails&utm_medium=referral&utm_campaign=readme)
---
[Installation](#installation)
- [Setup your API keys](#setup-your-api-keys)
- [Manually set your API version (optional)](#manually-set-your-api-version-optional)
[Setup your payment configuration](#setup-your-payment-configuration)
- [Configuring your plans and coupons](#configuring-your-plans-and-coupons)
[Stripe Elements](#stripe-elements)
- [Custom Elements](#custom-elements)
[Webhooks](#webhooks)
- [Signed Webhooks](#signed-webhooks)
- [Testing Signed Webhooks Locally](#testing-signed-webhooks-locally)
- [Disabling auto mount](#disabling-auto-mount)
- [Responding to webhooks](#responding-to-webhooks)
- [Critical and non-critical hooks](#critical-and-non-critical-hooks)
- [Filtering Callbacks](#filtering-callbacks)
- [Catchall Callback](#catchall-callback)
[Unit testing](#unit-testing)
[Thanks](#thanks)
[Code of Conduct](#code-of-conduct)
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'stripe-rails'
```
If you are going to be using [stripe.js][1] to securely collect credit card information
on the client, then you will need to add the stripe javascript tags into your template.
stripe-rails provides a helper to make this easy:
```erb
<%= stripe_javascript_tag %>
```
or, you can render it as a partial:
```erb
<%= render :partial => 'stripe/js' %>
```
In both cases, stripe-rails will choose a version of stripe.js appropriate for your
development environment and automatically configure it to use
your publishable API key. By default it uses `stripe-debug.js` for your `development`
environment and `stripe.js` for everything else, but you can manually configure it
per environment.
```ruby
config.stripe.debug_js = true # use stripe-debug.js
config.stripe.debug_js = false # use stripe.js
```
By default the helper renders the `v3` version of `stripe.js`. You can provide an
alternate version to the helper to generate the appropriate tag:
```erb
<%= stripe_javascript_tag(:v2) %>
```
### Setup your API keys.
You will need to configure your application to authenticate with stripe.com
using [your api key][1]. There are two methods to do this, you can either set the environment
variable `STRIPE_SECRET_KEY`:
```sh
export STRIPE_SECRET_KEY=sk_test_xxyyzz
```
or if you are on heroku:
```sh
heroku config:add STRIPE_SECRET_KEY=sk_test_xxyyzz
```
You can also set this value from inside ruby configuration code:
```ruby
config.stripe.secret_key = "sk_test_xxyyzz"
```
In either case, it is recommended that you *not* check in this value into source control.
You can verify that your api is set up and functioning properly by running the following command:
```sh
rake stripe:verify
```
If you are going to be using stripe.js, then you will also need to set the value of your
publishable key. A nice way to do it is to set your test publishable for all environments:
```ruby
# config/application.rb
# ...
config.stripe.publishable_key = 'pk_test_XXXYYYZZZ'
```
And then override it to use your live key in production only
```ruby
# config/environments/production.rb
# ...
config.stripe.publishable_key = 'pk_live_XXXYYYZZZ'
```
This key will be publicly visible on the internet, so it is ok to put in your source. If
you prefer to environment variables, you can also set `STRIPE_PUBLISHABLE_KEY`:
```sh
export STRIPE_PUBLISHABLE_KEY=pk_test_XXXYYYZZZ
```
If no API key is provided, `stripe-rails` will show a warning: "No stripe.com API key was configured ...". You can silence this warning by setting the `ignore_missing_secret_key` option to `true`:
```ruby
# config/environments/production.rb
# ...
config.stripe.ignore_missing_secret_key = true
```
### Manually set your API version (optional)
If you need to test a new API version in development, you can override the version number manually.
```ruby
# config/environments/development.rb
# ...
config.stripe.api_version = '2015-10-16'
```
## Setup your payment configuration
If you're using subscriptions, then you'll need to set up your application's payment plans
and discounts. `Stripe::Rails` lets you automate the management of these definitions from
within the application itself. To get started:
```sh
rails generate stripe:install
```
this will generate the configuration files containing your plan and coupon definitions:
```console
create config/stripe/products.rb
create config/stripe/plans.rb
create config/stripe/prices.rb
create config/stripe/coupons.rb
```
### Configuring your plans and coupons
Use the plan builder to define as many plans as you want in `config/stripe/plans.rb`
```ruby
Stripe.plan :silver do |plan|
plan.name = 'ACME Silver'
plan.amount = 699 # $6.99
plan.interval = 'month'
end
Stripe.plan :gold do |plan|
plan.name = 'ACME Gold'
plan.amount = 999 # $9.99
plan.interval = 'month'
end
Stripe.plan :bronze do |plan|
# Use an existing product id to prevent a new plan from
# getting created
plan.product_id = 'prod_XXXXXXXXXXXXXX'
plan.amount = 999 # $9.99
plan.interval = 'month'
# Use graduated pricing tiers
# ref: https://stripe.com/docs/api/plans/object#plan_object-tiers
plan.tiers = [
{
unit_amount: 1500,
up_to: 10
},
{
unit_amount: 1000,
up_to: 'inf'
}
]
plan.tiers_mode = 'graduated'
# set the usage type to 'metered'
plan.usage_type = 'metered'
end
```
This will define constants for these plans in the Stripe::Plans module so that you
can refer to them by reference as opposed to an id string.
```ruby
Stripe::Plans::SILVER # => 'silver: ACME Silver'
Stripe::Plans::GOLD # => 'gold: ACME Gold'
```
If you have to support an existing plan with a Stripe plan id that can not
be used as a Ruby constant, provide the plan id as a symbol when
defining the plan, but provide the name for the constant to define with `constant_name`:
```ruby
Stripe.plan "Silver-Plan".to_sym do |plan|
plan.constant_name = 'SILVER_PLAN' # <---
plan.name = 'ACME Silver'
plan.amount = 699
plan.interval = 'month'
end
Stripe::Plans::SILVER_PLAN # => will be defined
# Will map to plan :id => "Silver-Plan" on Stripe
```
**Note** - If you're planning on running `rake stripe:prepare` to
create your subscription plans, Stripe will restrict plan ids to match
this regexp (`/\A[a-zA-Z0-9_\-]+\z/`) when created via API but still
allows creation of plan ids that don't follow this restriction when
manually created on stripe.com.
Coupons are created in much the same way:
```ruby
Stripe.coupon :super_elite_free_vip do |coupon|
coupon.duration = 'forever'
coupon.percent_off = 100
coupon.max_redemptions = 5
end
```
As are Products:
```ruby
Stripe.product :primo do |product|
product.name = 'PRIMO as a service'
product.type = 'service'
product.statement_descriptor = 'PRIMO'
end
```
And Prices:
```ruby
Stripe.price :bronze do |price|
# Use an existing product id to prevent a new product from
# getting created
price.product_id = Stripe::Products::PRIMO.id
price.billing_scheme = 'tiered'
price.recurring = {
interval: 'month',
usage_type: 'metered'
}
# Use graduated pricing tiers
# ref: https://stripe.com/docs/api/prices/object#price_object-tiers
price.tiers = [
{
unit_amount: 1500,
up_to: 10
},
{
unit_amount: 1000,
up_to: 'inf'
}
]
price.tiers_mode = 'graduated'
end
````
To upload your plans, products, prices and coupons onto stripe.com, run:
```sh
rake stripe:prepare
```
This will create any plans, products, prices and coupons that do not currently exist, and treat as a NOOP any
objects that already exist, so you can run this command safely as many times as you wish. Now you can
use any of these objects in your application.
NOTE: You must destroy plans and prices manually from your stripe dashboard.
## Stripe Elements
Stripe::Rails allows you to easily include [Stripe Elements](https://stripe.com/payments/elements) in your application.
> Stripe Elements are rich, pre-built UI components that help you create your own pixel-perfect checkout flows across desktop and mobile.
Simply include the `stripe_elements_tag` anywhere below the `stripe_javascript_tag` and pass it the path to the controller action which will handle the Stripe token once the form is submitted:
```erb
<%= stripe_javascript_tag %>
<%= stripe_elements_tag submit_path: billing_path %>
```
Additionally, you can pass a block containing custom form elements to stripe_elements_tag:
## Custom Elements
> Stripe::Rails allows you to easily include your own custom form elements
> within the Stripe form by including those form elements in a block passed to
> `stripe_elements_tag`:
```erb
<%= stripe_javascript_tag %>
<%= stripe_elements_tag(submit_path: billing_path) do %>
<%= label_tag 'email', 'Email' %>
<%= text_field :user, :email %>
<% end %>
```
### Configuration options
Stripe::Rails comes bundled with default CSS and Javascript for Stripe elements, making it easy to drop in to your app. You can also specify your own assets paths:
```erb
<%= stripe_elements_tag submit_path: billing_path,
css_path: 'your/asset/path',
js_path: 'your/asset/path' %>
```
If you decide to use your own CSS and Javascript for Stripe Elements, please refer to the [Stripe elements docs](https://stripe.com/docs/stripe-js/elements/quickstart).
To change the form text you can add the following keys to your locale files
```yaml
# config/locales/en.yml
en:
stripe_rails:
elements:
label_text: Your label text
submit_button_text: Your button text
```
## Webhooks
Stripe::Rails automatically sets up your application to receive webhooks from stripe.com whenever
a payment event is generated. To enable this, you will need to configure your [stripe webhooks][3] to
point back to your application. By default, the webhook controller is mounted at '/stripe/events' so
you would want to enter in `http://myproductionapp.com/stripe/events` as your url for live mode,
and `http://mystagingapp.com/stripe/events` for your test mode.
If you want to mount the stripe engine somewhere else, you can do so by setting the `stripe.endpoint`
parameter. E.g.
```ruby
config.stripe.endpoint = '/payment/stripe-integration'
```
Your new webhook URL would then be `http://myproductionapp/payment/stripe-integration/events`
### Signed Webhooks
Validation of your webhook's signature uses your webhook endpoint signing secret.
Before you can verify signatures, you need to retrieve your endpoint’s secret from your
Stripe Dashboard. Select an endpoint for which you want to obtain
the secret, then select the Click to reveal button.
```ruby
# config/application.rb
# ...
config.stripe.signing_secrets = ['whsec_XXXYYYZZZ']
```
Each secret is unique to the endpoint to which it corresponds. If you use multiple endpoint,
you must obtain a secret for each one. After this setup, Stripe starts to sign each webhook
it sends to the endpoint. Because of this, we recommend setting this variable with environment
variables:
```sh
export STRIPE_SIGNING_SECRET=whsec_XXXYYYZZZ
export STRIPE_CONNECT_SIGNING_SECRET=whsec_AAABBBCCC
```
```ruby
config.stripe.signing_secrets = [ENV.fetch('STRIPE_SIGNING_SECRET'), ENV.fetch('STRIPE_CONNECT_SIGNING_SECRET')]
```
The first secret that successfully matches for each incoming webhook will be used to verify the incoming events.
#### Testing Signed Webhooks Locally
In order to test signed webhooks, you'll need to trigger test webhooks from your Stripe dashboard,
and configure your local environment to receive remote network requests. To do so, we recommend using
[ngrok](https://ngrok.com/) to configure a secure tunnel to `localhost`.
Once configured and running, `ngrok` will give you a unique URL which can be used to set up a webhook
endpoint. Webhook endpoints are configured in your Dashboard's [Webhook settings](https://dashboard.stripe.com/account/webhooks)
section. Make sure you are in **Test** mode and click `Add endpoint`, and provide your `ngrok` URL along with the `stripe.endpoint` suffix.
An example webhook URL would then be `https://bf2a5d21.ngrok.io/stripe/events`.
Once your endpoint is configured, you can reveal the **Signing secret**. This will need to be set
as documented above:
```ruby
# config/application.rb
# ...
config.stripe.signing_secrets = ['whsec_XXXYYYZZZ']
```
And you'll need to restart your rails server with:
```sh
rails restart
```
Now you're ready to click **Send test webhook**, and trigger whichever events you'd like to test from Stripe itself.
### Disabling auto mount
Sometimes, you don't want the stripe engine to be auto-mounted so that
you control *exactly* what priority it will take in your routing
table. This is especially important if you have a catch-all route
which should appear after all other routes. In order to disable
auto-mounting of the Stripe engine:
```ruby
# in application.rb
config.stripe.auto_mount = false
```
Then, you will have to manually mount the engine in your main application.
```ruby
# in your application's routes.rb:
mount Stripe::Engine => "/stripe"
```
### Responding to webhooks
Once you have your webhook URL configured you can respond to a stripe webhook *anywhere* in your
application just by including the Stripe::Callbacks module into your class and declaring a
callback with one of the callback methods. For example, to update a customer's payment status:
```ruby
class User < ActiveRecord::Base
include Stripe::Callbacks
after_customer_updated! do |customer, event|
user = User.find_by_stripe_customer_id(customer.id)
if customer.delinquent
user.is_account_current = false
user.save!
end
end
end
```
or to send an email with one of your customer's monthly invoices
```ruby
class InvoiceMailer < ActionMailer::Base
include Stripe::Callbacks
after_invoice_created! do |invoice, event|
user = User.find_by_stripe_customer(invoice.customer)
new_invoice(user, invoice).deliver
end
def new_invoice(user, invoice)
@user = user
@invoice = invoice
mail :to => user.email, :subject => '[Acme.com] Your new invoice'
end
end
```
**Note:** `Stripe::Callbacks` won't get included until the including class has been loaded. This is usually not an issue in the production environment as eager loading is enabled by default (`config.eager_load = true`). You may run into an issue in your development environment where eager loading is disabled by default.
If you don't wish to enable eager loading in development, you can configure the classes to be eager loaded like so
```ruby
# in your application's config/environments/development.rb
config.stripe.eager_load = 'account', 'module/some_class', 'etc'
```
This will ensure that callbacks will get loaded in those configured classes if eager loading is disabled.
The naming convention for the callback events is after__{callback_name}! where `callback_name`
is name of the stripe event with all `.` characters substituted with underscores. So, for
example, the stripe event `customer.discount.created` can be hooked by `after_customer_discount_created!`
and so on...
Each web hook is passed an instance of the stripe object to which the event corresponds
([`Stripe::Customer`][8], [`Stripe::Invoice`][9], [`Stripe::Charge`][10], etc...) as well as the [`Stripe::Event`][4] which contains metadata about the event being raised.
By default, the event is re-fetched securely from stripe.com to prevent damage to your system by
a malicious system spoofing real stripe events.
### Critical and non-critical hooks
So far, the examples have all used critical hooks, but in fact, each callback method comes in two flavors: "critical",
specified with a trailing `!` character, and "non-critical", which has no "bang" character at all. What
distinguishes one from the other is that _if an exception is raised in a critical callback, it will cause the entire webhook to fail_.
This will indicate to stripe.com that you did not receive the webhook at all, and that it should retry it again later until it
receives a successful response. On the other hand, there are some tasks that are more tangential to the payment work flow and aren't
such a big deal if they get dropped on the floor. For example, A non-critical hook can be used to do things like have a bot
notify your company's chatroom that something a credit card was successfully charged:
```ruby
class AcmeBot
include Stripe::Callbacks
after_charge_succeeded do |charge|
announce "Attention all Dudes and Dudettes. Ya'll are so PAID!!!"
end
end
```
Chances are that if you experience a momentary failure in connectivity to your chatroom, you don't want the whole payment notification to fail.
### Filtering Callbacks
Certain stripe events represent updates to existing data. You may want to only fire the event when certain attributes of that data
are updated. You can pass an `:only` option to your callback to filter to specify which attribute updates you're interested in. For
example, to warn users whenever their credit card has changed:
```ruby
class StripeMailer
include Stripe::Callbacks
after_customer_updated! :only => :active_card do |customer, evt|
your_credit_card_on_file_was_updated_are_you_sure_this_was_you(customer).deliver
end
end
```
Filters can be specified as an array as well:
```ruby
module Accounting
include Stripe::Callbacks
after_invoice_updated! :only => [:amount, :subtotal] do
# update our records
end
end
```
Alternatively, you can just pass a proc to filter the event manually. It will receive an instance of [`Stripe::Event`][4] as
its parameter:
```ruby
module StagingOnly
include Stripe::Callbacks
after_charge_succeeded! :only => proc {|charge, evt| unless evt.livemode} do |charge|
puts "FAKE DATA, PLEASE IGNORE!"
end
end
```
### Catchall Callback
The special 'stripe.event' callback will be invoked for every single event received from stripe.com. This can be useful for things
like logging and analytics:
```ruby
class StripeFirehose
include Stripe::Callbacks
after_stripe_event do |target, event|
# do something useful
end
end
```
See the [complete listing of all stripe events][5], and the [webhook tutorial][6] for more great information on this subject.
## Unit testing
If you want to test your callbacks, you can use the `Stripe::Rails::Testing` module to send mocked Stripe events.
```ruby
require 'stripe/rails/testing'
test "my callback handles new subscription" do
Stripe::Rails::Testing.send_event "customer.subscription.created"
# Assertions
end
```
You can also overwrite some event properties: ([More info](https://github.com/rebelidealist/stripe-ruby-mock#customizing-webhooks))
```ruby
require 'stripe/rails/testing'
test "my callback handles new subscription" do
Stripe::Rails::Testing.send_event "customer.subscription.created", {
:email => "john@doe.com",
:account_balance => 40
}
# Assertions
end
```
The default fixtures come from [the `stripe-ruby-mock` gem](https://github.com/rebelidealist/stripe-ruby-mock/tree/master/lib/stripe_mock/webhook_fixtures).
## Thanks
<a href="http://frontside.io"></a>
`Stripe::Rails` was originally developed with love and fondness by your friends at [Frontside][7]. They are available for your custom software development needs, including integration with stripe.com.
<a href="https://www.evercondo.com"></a>
`Stripe::Rails` has also been supported by the fine folks at [Evercondo][11], the next generation condo management software.
[1]: https://stripe.com/docs/stripe.js
[2]: https://manage.stripe.com/#account/apikeys
[3]: https://manage.stripe.com/#account/webhooks
[4]: https://stripe.com/docs/api?lang=ruby#events
[5]: https://stripe.com/docs/api?lang=ruby#event_types
[6]: https://stripe.com/docs/webhooks
[7]: http://frontside.io
[8]: https://stripe.com/docs/api?lang=ruby#customers
[9]: https://stripe.com/docs/api?lang=ruby#invoices
[10]: https://stripe.com/docs/api?lang=ruby#charges
[11]: https://www.evercondo.com
## Code of Conduct
Please note that this project is released with a Contributor Code of
Conduct. By participating in this project you agree to abide by its
terms, which can be found in the `CODE_OF_CONDUCT.md` file in this
repository.
================================================
FILE: Rakefile
================================================
#!/usr/bin/env rake
require "bundler/gem_tasks"
require 'rake/testtask'
Rake::TestTask.new(:spec) do |t|
t.libs << 'test'
t.pattern = 'test/**/*_spec.rb'
t.warning = false
t.verbose = false
end
task default: :spec
================================================
FILE: app/assets/stripe/stripe_elements.css
================================================
.StripeElement {
background-color: white;
height: 40px;
padding: 10px 12px;
border-radius: 4px;
border: 1px solid transparent;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.StripeElement--webkit-autofill {
background-color: #fefde5 !important;
}
================================================
FILE: app/assets/stripe/stripe_elements.js
================================================
// Create a Stripe client.
var stripe = Stripe(window.stripe_publishable_key);
// Create an instance of Elements.
var elements = stripe.elements();
var card = elements.create('card');
card.mount('#card-element');
card.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
var form = document.getElementById('stripe-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
}
});
})
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('stripe-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
================================================
FILE: app/controllers/stripe/application_controller.rb
================================================
module Stripe
class ApplicationController < ActionController::Base
skip_before_action(:verify_authenticity_token) if protect_from_forgery.any?
# is anything stripe wide?
end
end
================================================
FILE: app/controllers/stripe/events_controller.rb
================================================
module Stripe
class EventsController < ::Stripe::ApplicationController
include Stripe::EventDispatch
respond_to :json
def create
@event = dispatch_stripe_event(request)
head :ok
rescue JSON::ParserError => e
::Rails.logger.error e.message
head :bad_request, status: 400
rescue Stripe::SignatureVerificationError => e
::Rails.logger.error e.message
head :bad_request, status: 400
end
end
end
================================================
FILE: app/helpers/stripe/javascript_helper.rb
================================================
module Stripe
module JavascriptHelper
DEFAULT_STRIPE_JS_VERSION = 'v3'
def stripe_javascript_tag(stripe_js_version = DEFAULT_STRIPE_JS_VERSION)
stripe_js_version = stripe_js_version.to_s.downcase
render 'stripe/js', stripe_js_version: stripe_js_version
end
def stripe_elements_tag(submit_path:,
css_path: asset_path("stripe_elements.css"),
js_path: asset_path("stripe_elements.js"),
&block)
render partial: 'stripe/elements', locals: {
submit_path: submit_path,
label_text: t('stripe_rails.elements.label_text'),
submit_button_text: t('stripe_rails.elements.submit_button_text'),
css_path: css_path,
js_path: js_path,
block: block
}
end
end
end
================================================
FILE: app/models/stripe/event_dispatch.rb
================================================
module Stripe
module EventDispatch
def dispatch_stripe_event(request)
retrieve_stripe_event(request) do |evt|
target = evt.data.object
::Stripe::Callbacks.run_callbacks(evt, target)
end
end
def retrieve_stripe_event(request)
id = request.params['id']
body = request.body.read
sig_header = request.headers['HTTP_STRIPE_SIGNATURE']
endpoint_secrets = ::Rails.application.config.stripe.signing_secrets
if Object.const_defined?('Stripe::Webhook') && sig_header && endpoint_secrets
event = webhook_event(body, sig_header, endpoint_secrets)
else
event = Stripe::Event.retrieve(id)
end
yield event
end
private
def webhook_event(body, sig_header, endpoint_secrets)
endpoint_secrets.each_with_index do |secret, i|
begin
return ::Stripe::Webhook.construct_event(body, sig_header, secret.to_s)
rescue ::Stripe::SignatureVerificationError
raise if i == endpoint_secrets.length - 1
end
end
end
end
end
================================================
FILE: app/views/stripe/_elements.html.erb
================================================
<%= stylesheet_link_tag css_path, media: 'all' %>
<div>
<div id="card-errors">
</div>
<%= form_tag submit_path, id: "stripe-form" do %>
<% if local_assigns[:block] %>
<div id="stripe-rails-form-fields">
<%= capture(&local_assigns[:block]) %>
</div>
<% end %>
<%= label_tag :card_element, label_text %>
<div id="card-element"><!-- A Stripe Element will be inserted here. --></div>
<%= submit_tag submit_button_text %>
<% end %>
</div>
<script>
window.stripe_publishable_key = '<%= Rails.application.config.stripe.publishable_key %>'
</script>
<%= javascript_include_tag js_path, id: "stripe_elements_js" %>
================================================
FILE: app/views/stripe/_elements_js.html.erb
================================================
================================================
FILE: app/views/stripe/_js.html.erb
================================================
<%- stripe_js_version = Stripe::JavascriptHelper::DEFAULT_STRIPE_JS_VERSION if (!defined?(stripe_js_version) || stripe_js_version.nil?) %>
<%- case stripe_js_version %>
<%- when 'v1', 'v2' %>
<%- if ::Rails.application.config.stripe.debug_js %>
<script type="text/javascript" src="https://js.stripe.com/<%=stripe_js_version%>/stripe-debug.js"></script>
<%- else %>
<script type="text/javascript" src="https://js.stripe.com/<%=stripe_js_version%>/"></script>
<%- end %>
<script type="text/javascript">
Stripe.setPublishableKey("<%= Rails.application.config.stripe.publishable_key or fail 'No stripe.com publishable key found. Please set config.stripe.publishable_key in config/application.rb to one of your publishable keys, which can be found here: https://manage.stripe.com/#account/apikeys' %>")
</script>
<%- when 'v3' # the debug js for v3 isn't available %>
<script type="text/javascript" src="https://js.stripe.com/<%=stripe_js_version%>/"></script>
<script type="text/javascript">
var stripe = Stripe("<%= Rails.application.config.stripe.publishable_key or fail 'No stripe.com publishable key found. Please set config.stripe.publishable_key in config/application.rb to one of your publishable keys, which can be found here: https://manage.stripe.com/#account/apikeys' %>");
</script>
<%- end %>
================================================
FILE: config/locales/en.yml
================================================
en:
stripe_rails:
elements:
label_text: Credit or debit card
submit_button_text: Submit payment
================================================
FILE: config/routes.rb
================================================
Rails.application.routes.draw do
if Rails.application.config.stripe.auto_mount
mount Stripe::Engine => Rails.application.config.stripe.endpoint
end
end
Stripe::Engine.routes.draw do
resources :events, only: :create
end
================================================
FILE: gemfiles/gemfiles/rails70.gemfile
================================================
source :rubygems
gem 'rails', '~> 7.0.0'
gem 'rake'
gem 'responders'
gem 'stripe'
group :development, :test do
gem 'm'
end
group :test do
gem 'mocha'
gem 'simplecov', require: false
gem 'stripe-ruby-mock'
gem 'webmock'
# Required for system tests
gem 'capybara'
gem 'puma', '< 6' # https://github.com/teamcapybara/capybara/issues/2598
gem 'selenium-webdriver'
gem 'webdrivers'
end
================================================
FILE: gemfiles/rails60.gemfile
================================================
source :rubygems
gem 'rails', '~> 6.0.0'
gem 'rake'
gem 'responders'
gem 'stripe'
group :development, :test do
gem 'm'
end
group :test do
gem 'mocha'
gem 'simplecov', require: false
gem 'stripe-ruby-mock'
gem 'webmock'
# Required for system tests
gem 'capybara'
gem 'puma'
gem 'selenium-webdriver'
gem 'webdrivers'
end
================================================
FILE: gemfiles/rails61.gemfile
================================================
source :rubygems
gem 'rails', '~> 6.1.0'
gem 'rake'
gem 'responders'
gem 'stripe'
group :development, :test do
gem 'm'
end
group :test do
gem 'mocha'
gem 'simplecov', require: false
gem 'stripe-ruby-mock'
gem 'webmock'
# Required for system tests
gem 'capybara'
gem 'puma'
gem 'selenium-webdriver'
gem 'webdrivers'
end
================================================
FILE: gemfiles/rails70.gemfile
================================================
source :rubygems
gem 'rails', '~> 7.0.0'
gem 'rake'
gem 'responders'
gem 'stripe'
group :development, :test do
gem 'm'
end
group :test do
gem 'mocha'
gem 'simplecov', require: false
gem 'stripe-ruby-mock'
gem 'webmock'
# Required for system tests
gem 'capybara'
gem 'puma'
gem 'selenium-webdriver'
gem 'webdrivers'
end
================================================
FILE: gemfiles/rails71.gemfile
================================================
source :rubygems
gem 'rails', '~> 7.1.0'
gem 'rake'
gem 'responders'
gem 'stripe'
group :development, :test do
gem 'm'
end
group :test do
gem 'mocha'
gem 'simplecov', require: false
gem 'stripe-ruby-mock'
gem 'webmock'
# Required for system tests
gem 'capybara'
gem 'puma'
gem 'selenium-webdriver'
gem 'webdrivers'
end
================================================
FILE: lib/generators/stripe/install_generator.rb
================================================
module Stripe
class InstallGenerator < ::Rails::Generators::Base
source_root ::File.expand_path("../../templates", __FILE__)
desc "copy plans.rb"
def copy_plans_file
copy_file "products.rb", "config/stripe/products.rb"
copy_file "plans.rb", "config/stripe/plans.rb"
copy_file "prices.rb", "config/stripe/prices.rb"
copy_file "coupons.rb", "config/stripe/coupons.rb"
end
end
end
================================================
FILE: lib/generators/templates/coupons.rb
================================================
# This file contains descriptions of all your statically defined
# stripe coupons. You may wish to define unique one-off coupons
# elsewhere, but for ones you will use many times, and will be
# shared between users, this is a good place.
# Example
# Stripe::Coupons::Gold25 #=> 'gold25'
# Stripe.coupon :gold25 do |coupon|
# # specify if this coupon is useable 'once', 'forever', or 'repeating'
# coupon.duration = 'repeating'
#
# # absolute amount, in cents, to discount
# coupon.amount_off = 199
#
# # what currency to interpret the coupon amount
# coupon.currency = 'usd'
#
# # how long will this coupon last? (only valid for duration of 'repeating')
# coupon.duration_in_months = 6
#
# # percentage off
# coupon.percent_off = 25
#
# UTC timestamp specifying the last time at which the coupon can be redeemed
# coupon.redeem_by = (Time.now + 15.days).utc
#
# # How many times can this coupon be redeemed?
# coupon.max_redemptions = 10
# end
#
# Once you have your coupons defined, you can run
#
# rake stripe:prepare
#
# This will export any new coupons to stripe.com so that you can
# begin using them in your API calls. Any coupons found that are not in this
# file will be left as-is.
================================================
FILE: lib/generators/templates/plans.rb
================================================
# This file contains descriptions of all your stripe plans
# Example
# Stripe::Plans::PRIMO #=> 'primo'
# Stripe.plan :primo do |plan|
# # plan name as it will appear on credit card statements
# plan.name = 'Acme as a service PRIMO'
#
# # amount in cents. This is 6.99
# plan.amount = 699
#
# # currency to use for the plan (default 'usd')
# plan.currency = 'usd'
#
# # interval must be either 'day', 'week', 'month' or 'year'
# plan.interval = 'month'
#
# # only bill once every three months (default 1)
# plan.interval_count = 3
#
# # number of days before charging customer's card (default 0)
# plan.trial_period_days = 30
# end
# Once you have your plans defined, you can run
#
# rake stripe:prepare
#
# This will export any new plans to stripe.com so that you can
# begin using them in your API calls.
================================================
FILE: lib/generators/templates/prices.rb
================================================
# This file contains descriptions of all your stripe prices
# Example
# Stripe::Prices::LITE.lookup_key #=> 'lite'
# Prices will have a stripe generated id. The lookup_key will match the
# configuration below. You can fetch the ID or object from stripe:
#
# Stripe::Prices::LITE.stripe_id #=> 'price_0000sdfs2qfsdf'
# Stripe::Prices::LITE.stripe_object #=> #<Stripe::Price:0x3584 id=price_0000sdfs2qfsdf>...
# Prices are not deletable via the API, the `reset!` method will instead
# create a new price and transfer the lookup key to the new price.
# Stripe.price :lite do |price|
# # Prices may belong to a product, this will create a product along with the price
# price.name = 'Acme as a service LITE'
# # You can also specify an existing product ID
# # price.product_id = Stripe::Products::PRIMO.id
#
# # amount in cents. This is 6.99
# price.unit_amount = 699
#
# # currency to use for the price (default 'usd')
# price.currency = 'usd'
#
# price.recurring = {
# # interval must be either 'day', 'week', 'month' or 'year'
# interval: 'month',
# # only bill once every three months (default 1)
# interval_count: 3,
# # Must be either 'metered' or 'licensed'
# usage_type: 'metered',
# # Specifies a usage aggregation strategy for metered usage
# aggregate_usage: 'sum'
# }
#
# end
# Once you have your prices defined, you can run
#
# rake stripe:prepare
#
# This will export any new prices to stripe.com so that you can
# begin using them in your API calls.
================================================
FILE: lib/generators/templates/products.rb
================================================
# This file contains descriptions of all your stripe products
# Example
# Stripe::Products::PRIMO #=> 'primo'
# Stripe.product :primo do |product|
# # product's name as it will appear on credit card statements
# product.name = 'Acme as a service PRIMO'
#
# # Product, either 'service' or 'good'
# product.type = 'service'
# end
# Once you have your products defined, you can run
#
# rake stripe:prepare
#
# This will export any new products to stripe.com so that you can
# begin using them in your API calls.
================================================
FILE: lib/stripe/billing_tier.rb
================================================
module Stripe
module Plans
class BillingTier
include ActiveModel::Validations
validates_presence_of :up_to
validates_presence_of :flat_amount, if: ->(tier) { tier.unit_amount.nil? },
message: 'one of `flat_amount` or `unit_amount` must be specified!'
validates_presence_of :unit_amount, if: ->(tier) { tier.flat_amount.nil? },
message: 'one of `flat_amount` or `unit_amount` must be specified!'
validates_absence_of :flat_amount, if: ->(tier) { tier.unit_amount.present? },
message: 'only one of `flat_amount` or `unit_amount` should be specified!'
validates_absence_of :unit_amount, if: ->(tier) { tier.flat_amount.present? },
message: 'only one of `flat_amount` or `unit_amount` should be specified!'
attr_accessor :up_to, :flat_amount, :unit_amount
def initialize(attrs)
@up_to = attrs[:up_to]
@flat_amount = attrs[:flat_amount]
@unit_amount = attrs[:unit_amount]
end
def to_h
{
up_to: up_to,
flat_amount: flat_amount,
unit_amount: unit_amount
}.compact
end
end
end
end
================================================
FILE: lib/stripe/callbacks/builder.rb
================================================
module Stripe
module Callbacks
module Builder
extend ActiveSupport::Concern
included do
extend ActiveSupport::Concern
@critical_callbacks = Hash.new do |h, k|
h[k] = []
end
@noncritical_callbacks = Hash.new do |h, k|
h[k] = []
end
module ClassMethods
end
class << self
attr_reader :critical_callbacks, :noncritical_callbacks
def clear_callbacks!
critical_callbacks.clear
noncritical_callbacks.clear
end
def callback(name)
method_name = "after_#{name.gsub('.', '_')}"
self::ClassMethods.send(:define_method, method_name) do |options = {}, &block|
::Stripe::Callbacks::noncritical_callbacks[name] << ::Stripe::Callbacks.callback_matcher(options, block)
end
self::ClassMethods.send(:define_method, "#{method_name}!") do |options = {}, &block|
::Stripe::Callbacks::critical_callbacks[name] << ::Stripe::Callbacks.callback_matcher(options, block)
end
end
def callback_matcher(options, block)
case only = options[:only]
when Proc, Method
proc do |target, evt|
block.call(target, evt) if only.call(target, evt)
end
when Array, Set
stringified_keys = only.map(&:to_s)
proc do |target, evt|
stringified_previous_attributes_keys = evt.data.previous_attributes.keys.map(&:to_s)
intersection = stringified_previous_attributes_keys - stringified_keys
block.call(target, evt) if intersection != stringified_previous_attributes_keys
end
when nil
block
else
callback_matcher options.merge(:only => [only]), block
end
end
end
end
end
end
end
================================================
FILE: lib/stripe/callbacks.rb
================================================
require 'stripe/callbacks/builder'
module Stripe
module Callbacks
include Callbacks::Builder
callback 'account.updated'
callback 'account.application.deauthorized'
callback 'account.external_account.created'
callback 'account.external_account.deleted'
callback 'account.external_account.updated'
callback 'application_fee.created'
callback 'application_fee.refunded'
callback 'application_fee.refund.updated'
callback 'balance.available'
callback 'charge.captured'
callback 'charge.failed'
callback 'charge.pending'
callback 'charge.refunded'
callback 'charge.succeeded'
callback 'charge.updated'
callback 'charge.dispute.closed'
callback 'charge.dispute.created'
callback 'charge.dispute.funds_reinstated'
callback 'charge.dispute.funds_withdrawn'
callback 'charge.dispute.updated'
callback 'charge.refund.updated'
callback 'checkout.session.async_payment_failed'
callback 'checkout.session.async_payment_succeeded'
callback 'checkout.session.completed'
callback 'checkout.session.expired'
callback 'coupon.created'
callback 'coupon.deleted'
callback 'coupon.updated'
callback 'customer.created'
callback 'customer.deleted'
callback 'customer.updated'
callback 'customer.discount.created'
callback 'customer.discount.deleted'
callback 'customer.discount.updated'
callback 'customer.source.created'
callback 'customer.source.deleted'
callback 'customer.source.expiring'
callback 'customer.source.updated'
callback 'customer.subscription.created'
callback 'customer.subscription.deleted'
callback 'customer.subscription.paused'
callback 'customer.subscription.pending_update_applied'
callback 'customer.subscription.pending_update_expired'
callback 'customer.subscription.resumed'
callback 'customer.subscription.trial_will_end'
callback 'customer.subscription.updated'
callback 'file.created'
callback 'invoice.created'
callback 'invoice.deleted'
callback 'invoice.finalization_failed'
callback 'invoice.finalized'
callback 'invoice.marked_uncollectible'
callback 'invoice.overdue'
callback 'invoice.paid'
callback 'invoice.payment_action_required'
callback 'invoice.payment_failed'
callback 'invoice.payment_succeeded'
callback 'invoice.sent'
callback 'invoice.upcoming'
callback 'invoice.updated'
callback 'invoice.voided'
callback 'invoice.will_be_due'
callback 'invoiceitem.created'
callback 'invoiceitem.deleted'
callback 'invoiceitem.updated'
callback 'order.created'
callback 'order.payment_failed'
callback 'order.payment_succeeded'
callback 'order.updated'
callback 'order_return.created'
callback 'payment_intent.amount_capturable_updated'
callback 'payment_intent.canceled'
callback 'payment_intent.created'
callback 'payment_intent.payment_failed'
callback 'payment_intent.processing'
callback 'payment_intent.requires_action'
callback 'payment_intent.succeeded'
callback 'payment_method.attached'
callback 'payment_method.card_automatically_updated'
callback 'payment_method.detached'
callback 'payment_method.updated'
callback 'payout.canceled'
callback 'payout.created'
callback 'payout.failed'
callback 'payout.paid'
callback 'payout.updated'
callback 'plan.created'
callback 'plan.deleted'
callback 'plan.updated'
callback 'price.created'
callback 'price.deleted'
callback 'price.updated'
callback 'product.created'
callback 'product.deleted'
callback 'product.updated'
callback 'recipient.created'
callback 'recipient.deleted'
callback 'recipient.updated'
callback 'review.closed'
callback 'review.opened'
callback 'setup_intent.canceled'
callback 'setup_intent.created'
callback 'setup_intent.requires_action'
callback 'setup_intent.setup_failed'
callback 'setup_intent.succeeded'
callback 'sigma.scheduled_query_run.created'
callback 'sku.created'
callback 'sku.deleted'
callback 'sku.updated'
callback 'source.canceled'
callback 'source.chargeable'
callback 'source.failed'
callback 'source.transaction.created'
callback 'subscription_schedule.aborted'
callback 'subscription_schedule.canceled'
callback 'subscription_schedule.completed'
callback 'subscription_schedule.created'
callback 'subscription_schedule.expiring'
callback 'subscription_schedule.released'
callback 'subscription_schedule.updated'
callback 'tax_rate.created'
callback 'tax_rate.updated'
callback 'transfer.created'
callback 'transfer.reversed'
callback 'transfer.updated'
callback 'ping'
callback 'stripe.event'
class << self
def run_callbacks(evt, target)
_run_callbacks evt.type, evt, target
_run_callbacks 'stripe.event', evt, target
end
def _run_callbacks(type, evt, target)
run_critical_callbacks type, evt, target
run_noncritical_callbacks type, evt, target
end
def run_critical_callbacks(type, evt, target)
::Stripe::Callbacks::critical_callbacks[type].each do |callback|
callback.call(target, evt)
end
end
def run_noncritical_callbacks(type, evt, target)
::Stripe::Callbacks::noncritical_callbacks[type].each do |callback|
begin
callback.call(target, evt)
rescue Exception => e
::Rails.logger.error e.message
::Rails.logger.error e.backtrace.join("\n")
end
end
end
end
end
end
================================================
FILE: lib/stripe/configuration_builder.rb
================================================
require "active_model"
module Stripe
module ConfigurationBuilder
extend ActiveSupport::Concern
included do
class << self
def configuration_for(class_id, &block)
@_configuration_storage = "@#{class_id.to_s.pluralize}"
instance_variable_set(@_configuration_storage, {})
configuration_class = Class.new(Stripe::ConfigurationBuilder::Configuration)
const_set(:Configuration, configuration_class)
configuration_class.class_eval(&block)
stripe_class = Stripe.const_get(class_id.to_s.camelize)
stripe_configuration_class = self
send(:define_method, class_id) do |id, &block|
config = configuration_class.new(id, stripe_class, stripe_configuration_class)
block.call config
config.finalize!
end
::Stripe.send(:extend, self)
end
def configurations
instance_variable_get(@_configuration_storage)
end
def all
configurations.values
end
def [](key)
configurations[key.to_s]
end
def []=(key, value)
configurations[key.to_s] = value
end
def put!
all.each(&:put!)
end
def reset!
all.each(&:reset!)
end
end
end
class Configuration
include ActiveModel::Validations
attr_reader :id
def initialize(id, stripe_class, stripe_configuration_class)
@id = id
@stripe_class = stripe_class
@stripe_configuration_class = stripe_configuration_class
end
def finalize!
validate!
globalize!
end
def validate!
fail Stripe::InvalidConfigurationError, errors if invalid?
end
def globalize!
id_to_use = @constant_name || @id
@stripe_configuration_class[id_to_use.to_s.downcase] = self
@stripe_configuration_class.const_set(id_to_use.to_s.upcase, self)
end
def put!
if exists?
puts "[EXISTS] - #{@stripe_class}:#{@id}" unless Stripe::Engine.testing
else
object = @stripe_class.create({:id => @id}.merge compact_create_options)
puts "[CREATE] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
end
end
def reset!
if object = exists?
object.delete
end
object = @stripe_class.create({:id => @id}.merge compact_create_options)
puts "[RESET] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
end
def compact_create_options
create_options.delete_if { |_, v| v.nil? }
end
def to_s
@id.to_s
end
def exists?
@stripe_class.retrieve(to_s)
rescue Stripe::InvalidRequestError
false
end
end
end
class InvalidConfigurationError < StandardError
attr_reader :errors
def initialize(errors)
super errors.messages
@errors = errors
end
end
end
================================================
FILE: lib/stripe/coupons.rb
================================================
module Stripe
module Coupons
include ConfigurationBuilder
configuration_for :coupon do
attr_accessor :name, :duration, :amount_off, :currency, :duration_in_months, :max_redemptions, :percent_off, :redeem_by
validates_presence_of :id, :duration
validates_presence_of :duration_in_months, :if => :repeating?
validates_inclusion_of :duration, :in => %w(forever once repeating), :message => "'%{value}' is not one of 'forever', 'once' or 'repeating'"
validates_inclusion_of :percent_off, in: 1..100, unless: ->(coupon) {coupon.percent_off.nil?}
validates_numericality_of :percent_off, :greater_than => 0, unless: ->(coupon) {coupon.percent_off.nil?}
validates_numericality_of :duration_in_months, :greater_than => 0, :if => :repeating?
validates_numericality_of :max_redemptions, greater_than: 0, unless: ->(coupon) {coupon.max_redemptions.nil?}
def initialize(*args)
super
@currency = 'usd'
@max_redemptions = 1
end
def repeating?
duration == 'repeating'
end
def create_options
{
:name => name,
:duration => duration,
:percent_off => percent_off,
:amount_off => amount_off,
:currency => currency,
:duration_in_months => duration_in_months,
:max_redemptions => max_redemptions,
:redeem_by => redeem_by
}
end
end
end
end
================================================
FILE: lib/stripe/current_api_version.rb
================================================
class CurrentApiVersion
def self.call
Stripe.api_version || begin
resp, _ = Stripe::Plan.request(:get, Stripe::Plan.resource_url)
resp.http_headers['stripe-version']
end
end
def self.after_switch_to_products_in_plans?
Date.parse(call) >= Date.parse('2018-02-05')
end
end
================================================
FILE: lib/stripe/engine.rb
================================================
require 'stripe'
module Stripe
class Engine < ::Rails::Engine
isolate_namespace Stripe
class << self
attr_accessor :testing
end
stripe_config = config.stripe = Struct.new(:api_base, :api_version, :secret_key, :ignore_missing_secret_key, :verify_ssl_certs, :signing_secret, :signing_secrets, :publishable_key, :endpoint, :debug_js, :auto_mount, :eager_load, :open_timeout, :read_timeout) do
# for backwards compatibility treat signing_secret as an alias for signing_secrets
def signing_secret=(value)
self.signing_secrets = value.nil? ? value : Array(value)
end
def signing_secret
self.signing_secrets && self.signing_secrets.first
end
end.new
def stripe_config.api_key=(key)
warn "[DEPRECATION] to align with stripe nomenclature, stripe.api_key has been renamed to config.stripe.secret_key"
self.secret_key = key
end
initializer 'stripe.configure.defaults', :before => 'stripe.configure' do |app|
stripe = app.config.stripe
stripe.secret_key ||= ENV['STRIPE_SECRET_KEY']
stripe.publishable_key ||= ENV['STRIPE_PUBLISHABLE_KEY']
stripe.endpoint ||= '/stripe'
stripe.auto_mount = true if stripe.auto_mount.nil?
stripe.eager_load ||= []
if stripe.debug_js.nil?
stripe.debug_js = ::Rails.env.development?
end
end
initializer 'stripe.configure' do |app|
[:api_base, :verify_ssl_certs, :api_version, :open_timeout, :read_timeout].each do |key|
value = app.config.stripe.send(key)
Stripe.send("#{key}=", value) unless value.nil?
end
secret_key = app.config.stripe.secret_key
Stripe.api_key = secret_key unless secret_key.nil?
$stderr.puts <<-MSG unless Stripe.api_key || app.config.stripe.ignore_missing_secret_key
No stripe.com API key was configured for environment #{::Rails.env}! this application will be
unable to interact with stripe.com. You can set your API key with either the environment
variable `STRIPE_SECRET_KEY` (recommended) or by setting `config.stripe.secret_key` in your
environment file directly.
MSG
end
eager_load_classes = -> class_names {
class_names.each do |constant|
begin
constant.to_s.camelize.constantize
rescue NameError
require constant
end
end
}
initializer 'stripe.callbacks.clear_after_unload' do |app|
# Skip Rails 4 for now.
next unless app.respond_to?(:reloader)
# Clear callbacks after all autoloaded classes are removed.
# This prevents duplicate callbacks being added during development.
app.reloader.after_class_unload do
::Stripe::Callbacks.clear_callbacks!
eager_load_classes.call(app.config.stripe.eager_load)
end
end
initializer 'stripe.callbacks.eager_load' do |app|
app.config.after_initialize do
eager_load_classes.call(app.config.stripe.eager_load)
end
end
config.to_prepare do
ActiveSupport.on_load :action_controller do
# ActionController::API does not have a helper method
if respond_to?(:helper)
helper Stripe::JavascriptHelper
end
end
end
initializer 'stripe.plans_and_coupons' do |app|
for configuration in %w(products plans coupons prices)
path = app.root.join("config/stripe/#{configuration}.rb")
load path if path.exist?
end
end
initializer 'stripe.assets.precompile' do |app|
if app.config.respond_to?(:assets)
app.config.assets.precompile += %w( stripe_elements.js stripe_elements.css )
end
end
rake_tasks do
load 'stripe/rails/tasks.rake'
end
end
end
================================================
FILE: lib/stripe/plans.rb
================================================
module Stripe
module Plans
include ConfigurationBuilder
configuration_for :plan do
attr_accessor :active,
:aggregate_usage,
:amount,
:billing_scheme,
:constant_name,
:currency,
:interval,
:interval_count,
:metadata,
:name,
:nickname,
:product_id,
:statement_descriptor,
:tiers,
:tiers_mode,
:transform_usage,
:trial_period_days,
:usage_type
validates_presence_of :id, :currency
validates_presence_of :amount, unless: ->(p) { p.billing_scheme == 'tiered' }
validates_absence_of :transform_usage, if: ->(p) { p.billing_scheme == 'tiered' }
validates_presence_of :tiers_mode, if: ->(p) { p.billing_scheme == 'tiered' }
validates_inclusion_of :interval,
in: %w(day week month year),
message: "'%{value}' is not one of 'day', 'week', 'month' or 'year'"
validates :statement_descriptor, length: { maximum: 22 }
validates :active, inclusion: { in: [true, false] }, allow_nil: true
validates :usage_type, inclusion: { in: %w{ metered licensed } }, allow_nil: true
validates :billing_scheme, inclusion: { in: %w{ per_unit tiered } }, allow_nil: true
validates :aggregate_usage, inclusion: { in: %w{ sum last_during_period last_ever max } }, allow_nil: true
validates :tiers_mode, inclusion: { in: %w{ graduated volume } }, allow_nil: true
validate :name_or_product_id
validate :aggregate_usage_must_be_metered, if: ->(p) { p.aggregate_usage.present? }
validate :valid_constant_name, unless: ->(p) { p.constant_name.nil? }
# validations for when using tiered billing
validate :tiers_must_be_array, if: ->(p) { p.tiers.present? }
validate :billing_scheme_must_be_tiered, if: ->(p) { p.tiers.present? }
validate :validate_tiers, if: ->(p) { p.billing_scheme == 'tiered' }
def initialize(*args)
super(*args)
@currency = 'usd'
@interval_count = 1
@trial_period_days = 0
end
private
def aggregate_usage_must_be_metered
errors.add(:aggregate_usage, 'usage_type must be metered') unless (usage_type == 'metered')
end
def name_or_product_id
errors.add(:base, 'must have a product_id or a name') unless (@product_id.present? ^ @name.present?)
end
def billing_scheme_must_be_tiered
errors.add(:billing_scheme, 'must be set to `tiered` when specifying `tiers`') unless billing_scheme == 'tiered'
end
def tiers_must_be_array
errors.add(:tiers, 'must be an Array') unless tiers.is_a?(Array)
end
def billing_tiers
@billing_tiers = tiers.map { |t| Stripe::Plans::BillingTier.new(t) } if tiers
end
def validate_tiers
billing_tiers.all?(&:valid?)
end
module ConstTester; end
def valid_constant_name
ConstTester.const_set(constant_name.to_s.upcase, constant_name)
ConstTester.send(:remove_const, constant_name.to_s.upcase.to_sym)
rescue NameError
errors.add(:constant_name, 'is not a valid Ruby constant name.')
end
def create_options
if CurrentApiVersion.after_switch_to_products_in_plans?
default_create_options
else
create_options_without_products
end
end
def default_create_options
{
currency: currency,
product: product_options,
amount: amount,
interval: interval,
interval_count: interval_count,
trial_period_days: trial_period_days,
metadata: metadata,
usage_type: usage_type,
aggregate_usage: aggregate_usage,
billing_scheme: billing_scheme,
nickname: nickname,
tiers: tiers ? tiers.map(&:to_h) : nil,
tiers_mode: tiers_mode,
transform_usage: transform_usage
}.compact
end
def product_options
product_id.presence || { name: name, statement_descriptor: statement_descriptor }
end
# Note: these options serve an older API, as such they should
# probably never be updated.
def create_options_without_products
{
currency: currency,
name: name,
amount: amount,
interval: interval,
interval_count: interval_count,
trial_period_days: trial_period_days,
metadata: metadata,
statement_descriptor: statement_descriptor
}
end
end
end
end
================================================
FILE: lib/stripe/prices.rb
================================================
module Stripe
module Prices
include ConfigurationBuilder
VALID_TIME_UNITS = %i(day week month year)
configuration_for :price do
attr_reader :lookup_key
attr_accessor :active,
:billing_scheme,
:constant_name,
:currency,
:metadata,
:name,
:nickname,
:object,
:product_id,
:recurring,
:statement_descriptor,
:tax_behavior,
:tiers,
:tiers_mode,
:transform_quantity,
:type,
:unit_amount
validates_presence_of :id, :currency
validates_presence_of :unit_amount, unless: ->(p) { p.billing_scheme == 'tiered' }
validates_absence_of :transform_quantity, if: ->(p) { p.billing_scheme == 'tiered' }
validates_presence_of :tiers_mode, :tiers, if: ->(p) { p.billing_scheme == 'tiered' }
validates_numericality_of :recurring_interval_count, allow_nil: true
validates_inclusion_of :recurring_interval,
in: VALID_TIME_UNITS.collect(&:to_s),
message: "'%{value}' is not one of #{VALID_TIME_UNITS.to_sentence(last_word_connector: ', or ')}",
if: ->(p) { p.recurring.present? }
validates :statement_descriptor, length: { maximum: 22 }
validates :active, inclusion: { in: [true, false] }, allow_nil: true
validates :billing_scheme, inclusion: { in: %w{ per_unit tiered } }, allow_nil: true
validates :recurring_aggregate_usage, inclusion: { in: %w{ sum last_during_period last_ever max } }, allow_nil: true
validates :recurring_usage_type, inclusion: { in: %w{ metered licensed } }, allow_nil: true
validates :tax_behavior, inclusion: { in: %w{ inclusive exclusive unspecified } }, allow_nil: true
validates :tiers_mode, inclusion: { in: %w{ graduated volume } }, allow_nil: true
validate :name_or_product_id
validate :recurring_aggregate_usage_must_be_metered, if: ->(p) { p.recurring_aggregate_usage.present? }
validate :recurring_interval_count_maximum, if: ->(p) { p.recurring_interval_count.present? }
validate :valid_constant_name, unless: ->(p) { p.constant_name.nil? }
# validations for when using tiered billing
validate :tiers_must_be_array, if: ->(p) { p.tiers.present? }
validate :billing_scheme_must_be_tiered, if: ->(p) { p.tiers.present? }
validate :validate_tiers, if: ->(p) { p.billing_scheme == 'tiered' }
def initialize(*args)
super(*args)
@currency = 'usd'
@lookup_key = @id.to_s
@recurring = (recurring || {}).symbolize_keys
end
# We're overriding a handful of the Configuration methods so that
# we find and create by lookup_key instead of by ID. The ID is assigned
# by stripe and out of our control
def put!
if exists?
puts "[EXISTS] - #{@stripe_class}:#{@id}:#{stripe_id}" unless Stripe::Engine.testing
else
object = @stripe_class.create({:lookup_key => @lookup_key}.merge compact_create_options)
puts "[CREATE] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
end
end
# You can't delete prices, but you can transfer the lookup key to a new price
def reset!
object = @stripe_class.create(reset_options)
puts "[RESET] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
end
def exists?
stripe_object.presence
rescue Stripe::InvalidRequestError
false
end
def stripe_object
@stripe_class.list({lookup_keys: [@lookup_key]}).data.first.presence || nil
rescue Stripe::InvalidRequestError
nil
end
def stripe_id
@stripe_id ||= stripe_object.try(:id)
end
def recurring_interval
recurring[:interval]
end
def recurring_aggregate_usage
recurring[:aggregate_usage]
end
def recurring_usage_type
recurring[:usage_type]
end
def recurring_interval_count
recurring[:interval_count]
end
private
def recurring_aggregate_usage_must_be_metered
errors.add(:recurring_aggregate_usage, 'recurring[:usage_type] must be metered') unless (recurring_usage_type == 'metered')
end
def recurring_interval_count_maximum
time_unit = recurring_interval.to_sym
return unless VALID_TIME_UNITS.include?(time_unit) && recurring_interval_count.respond_to?(time_unit)
too_long = recurring_interval_count.send(time_unit) > 1.year
errors.add(:recurring_interval_count, 'recurring[:interval_count] Maximum is one year (1 year, 12 months, or 52 weeks') if too_long
end
def name_or_product_id
errors.add(:base, 'must have a product_id or a name') unless (@product_id.present? ^ @name.present?)
end
def billing_scheme_must_be_tiered
errors.add(:billing_scheme, 'must be set to `tiered` when specifying `tiers`') unless billing_scheme == 'tiered'
end
def tiers_must_be_array
errors.add(:tiers, 'must be an Array') unless tiers.is_a?(Array)
end
def billing_tiers
@billing_tiers = tiers.map { |t| Stripe::Plans::BillingTier.new(t) } if tiers
end
def validate_tiers
billing_tiers.all?(&:valid?)
end
module ConstTester; end
def valid_constant_name
ConstTester.const_set(constant_name.to_s.upcase, constant_name)
ConstTester.send(:remove_const, constant_name.to_s.upcase.to_sym)
rescue NameError
errors.add(:constant_name, 'is not a valid Ruby constant name.')
end
def reset_options
existing_object = stripe_object
# Lookup and set the existing product ID if unset
@product_id ||= existing_object.product if existing_object.present?
{ transfer_lookup_key: existing_object.present? }.merge(compact_create_options)
end
def create_options
{
currency: currency,
unit_amount: unit_amount,
active: active,
metadata: metadata,
nickname: nickname.presence || @lookup_key,
recurring: recurring.compact,
tiers: tiers ? tiers.map(&:to_h) : nil,
tiers_mode: tiers_mode,
billing_scheme: billing_scheme,
lookup_key: @lookup_key,
tax_behavior: tax_behavior,
transform_quantity: transform_quantity,
}.merge(product_options).compact
end
def product_options
if product_id.present?
{ product: product_id }
else
{
product_data: { name: name, statement_descriptor: statement_descriptor }
}
end
end
end
end
end
================================================
FILE: lib/stripe/products.rb
================================================
module Stripe
module Products
include ConfigurationBuilder
configuration_for :product do
attr_accessor :name,
:type,
:active,
:attributes,
:description,
:caption,
:metadata,
:shippable,
:unit_label,
:url,
:statement_descriptor
validates_presence_of :name, :type
validates :statement_descriptor, length: { maximum: 22 }
validates :active, :shippable, inclusion: { in: [true, false] }, allow_nil: true
validates :type, inclusion: { in: %w(service good) }
validates :caption, :description, :shippable, :url, absence: true, unless: :good?
validates :statement_descriptor, absence: true, unless: :service?
private
def good?
type == 'good'
end
def service?
type == 'service'
end
def create_options
{
name: name,
type: type,
active: active,
attributes: attributes,
description: description,
caption: caption,
metadata: metadata,
shippable: shippable,
unit_label: unit_label,
url: url,
statement_descriptor: statement_descriptor
}
end
end
end
end
================================================
FILE: lib/stripe/rails/tasks.rake
================================================
namespace :stripe do
desc 'verify your stripe.com authentication configuration'
task 'verify' => :environment do
begin
Stripe::Plan.list
puts "[OK] - connection to stripe.com is functioning properly"
rescue Stripe::AuthenticationError => e
puts "[FAIL] - authentication failed"
end
end
task 'products:prepare' => 'environment' do
if CurrentApiVersion.after_switch_to_products_in_plans?
Stripe::Products.put!
else
puts '[SKIPPED] Current API version does not support Products'
end
end
task 'plans:prepare' => 'environment' do
Stripe::Plans.put!
end
task 'coupons:prepare' => 'environment' do
Stripe::Coupons.put!
end
desc 'delete and redefine all coupons defined in config/stripe/coupons.rb'
task 'coupons:reset!' => 'environment' do
Stripe::Coupons.reset!
end
task 'prices:prepare' => 'environment' do
Stripe::Prices.put!
end
desc 'delete and redefine all prices defined in config/stripe/prices.rb'
task 'prices:reset!' => 'environment' do
Stripe::Prices.reset!
end
desc "create all plans and coupons defined in config/stripe/{products|plans|prices|coupons}.rb"
task 'prepare' => ['products:prepare', 'plans:prepare', 'prices:prepare', 'coupons:prepare']
end
================================================
FILE: lib/stripe/rails/testing.rb
================================================
begin
require 'stripe_mock'
rescue LoadError
warn %q{Please add "gem 'stripe-ruby-mock', group: 'test'"" to the Gemfile to use Stripe::Rails::Testing"}
exit
end
require 'stripe/callbacks'
module Stripe
module Rails
module Testing
def self.send_event(event, properties = {})
evt = StripeMock.mock_webhook_event(event, properties)
target = evt.data.object
::Stripe::Callbacks.run_callbacks(evt, target)
end
end
end
end
================================================
FILE: lib/stripe/rails/version.rb
================================================
module Stripe
module Rails
VERSION = '2.6.0'.freeze
end
end
================================================
FILE: lib/stripe/rails.rb
================================================
require 'responders'
require "stripe/rails/version"
require 'stripe/engine'
require 'stripe/configuration_builder'
require 'stripe/current_api_version'
require 'stripe/plans'
require 'stripe/prices'
require 'stripe/billing_tier'
require 'stripe/coupons'
require 'stripe/products'
require 'stripe/callbacks'
================================================
FILE: lib/stripe-rails.rb
================================================
require 'stripe/rails'
================================================
FILE: stripe-rails.gemspec
================================================
require File.expand_path('lib/stripe/rails/version', __dir__)
Gem::Specification.new do |gem|
gem.authors = ["Charles Lowell", "Nola Stowe", "SengMing Tan"]
gem.email = ["sengming@sanemen.com"]
gem.description = "A gem to integrate stripe into your rails app"
gem.summary = "A gem to integrate stripe into your rails app"
gem.homepage = "https://github.com/tansengming/stripe-rails"
gem.license = 'MIT'
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "stripe-rails"
gem.require_paths = ["lib"]
gem.version = Stripe::Rails::VERSION
gem.add_dependency 'rails', '>= 5.1'
gem.add_dependency 'stripe', '>= 3.15.0'
gem.add_dependency 'responders'
end
================================================
FILE: test/callbacks_spec.rb
================================================
require 'spec_helper'
describe Stripe::Callbacks do
include Rack::Test::Methods
include CallbackHelpers
let(:app) { Rails.application }
let(:event) { JSON.parse(File.read File.expand_path('event.json', __dir__)) }
let(:invoice) { JSON.parse(File.read File.expand_path('invoice.json', __dir__)) }
let(:content) { event }
let(:observer) { Class.new }
before do
header 'Accept', 'application/json'
header 'Content-Type', 'application/json'
observer.include Stripe::Callbacks
event['data']['object'] = invoice
self.type = content['type']
end
after { ::Stripe::Callbacks.clear_callbacks! }
subject { post 'stripe/events', JSON.pretty_generate(content) }
describe 'defined with a bang' do
let(:callback) { :after_invoice_payment_succeeded! }
before { run_callback_with(callback) {|target, e| @event = e; @target = target} }
describe 'when it is invoked for the invoice.payment_succeeded event' do
it 'is invoked for the invoice.payment_succeeded event' do
subject
_(@event).wont_be_nil
_(@event.type).must_equal 'invoice.payment_succeeded'
_(@target.total).must_equal 6999
end
end
describe 'when the invoked.payment_failed webhook is called' do
before { self.type = 'invoked.payment_failed' }
it 'the invoice.payment_succeeded callback is not invoked' do
subject
_(@event).must_be_nil
end
end
describe 'if it raises an exception' do
before { run_callback_with(callback) { fail } }
it 'causes the whole webhook to fail' do
_(-> { subject }).must_raise RuntimeError
end
end
end
describe 'defined without a bang and raising an exception' do
let(:callback) { :after_invoice_payment_succeeded }
before { run_callback_with(callback) { fail } }
it 'does not cause the webhook to fail' do
subject
_(last_response.status).must_be :>=, 200
_(last_response.status).must_be :<, 300
end
end
describe 'the after_stripe_event callback to catch any event' do
let(:events) { [] }
before { run_callback_with(:after_stripe_event) { |_, evt| events << evt } }
describe 'when it gets invoked for a standard event' do
before { self.type = 'invoice.payment_failed' }
it 'it will be run' do
subject
_(events.first.type).must_equal 'invoice.payment_failed'
end
end
describe 'when it gets invoked for an arbitrary event' do
before { self.type = 'foo.bar.baz' }
it 'it will be run' do
subject
_(events.first.type).must_equal 'foo.bar.baz'
end
end
end
describe 'filtering on specific changed attributes' do
events = nil
before do
events = []
self.type = 'invoice.updated'
@stubbed_event.data.previous_attributes = {}
end
describe 'specified as an single symbol' do
before do
observer.class_eval do
after_invoice_updated! :only => :closed do |invoice, evt|
events << evt
end
end
end
describe 'when a prior attribute was not specified' do
it 'does not fire events' do
subject
_(events.length).must_equal 0
end
end
describe 'when a prior attribute was specified' do
before { @stubbed_event.data.previous_attributes['closed'] = true }
it 'fires events' do
subject
_(events.length).must_equal 1
end
end
end
describe 'specified as an array' do
before do
observer.class_eval do
after_invoice_updated! :only => [:currency, :subtotal] do |invoice, evt|
events << evt
end
end
end
describe 'when a prior attribute was not specified' do
it 'does not fire events' do
subject
_(events.length).must_equal 0
end
end
describe 'when prior attributes were specified' do
before { @stubbed_event.data.previous_attributes['subtotal'] = 699 }
it 'fire events' do
subject
_(events.length).must_equal 1
end
end
end
describe 'specified as a lambda' do
before do
observer.class_eval do
after_invoice_updated :only => proc {|target, evt| evt.data.previous_attributes.to_hash.has_key? :closed} do |i,e|
events << e
end
end
end
describe 'when the lambda is not true' do
it 'does not fire events' do
subject
_(events.length).must_equal 0
end
end
describe 'when the lambda is not true' do
before { @stubbed_event.data.previous_attributes['closed'] = 'false' }
it 'fires events' do
subject
_(events.length).must_equal 1
end
end
end
end
describe 'with forgery protection enabled' do
before do
ActionController::Base.allow_forgery_protection = true
ActionController::Base.protect_from_forgery with: :exception
end
after { ActionController::Base.allow_forgery_protection = false }
it { subject } # must_not raise error
end
end
================================================
FILE: test/coupon_builder_spec.rb
================================================
require 'spec_helper'
describe 'building coupons' do
before do
Stripe::Coupons.configurations.clear
end
describe 'default values' do
before do
@coupon = Stripe.coupon(:firesale) do |coupon|
coupon.name = '100% OFF FIRESALE'
coupon.duration = 'once'
coupon.duration_in_months = 100
coupon.amount_off = 100
end
end
it "allows a single redemption by default" do
_(@coupon.max_redemptions).must_equal 1
end
end
describe 'simply' do
before do
@now = Time.now.utc
Stripe.coupon(:gold25) do |coupon|
coupon.duration = 'repeating'
coupon.duration_in_months = 10
coupon.amount_off = 100
coupon.currency = 'USD'
coupon.max_redemptions = 3
coupon.percent_off = 25
coupon.redeem_by = @now
end
end
after {Stripe::Coupons.send(:remove_const, :GOLD25)}
it 'is accessible via hash lookup (symbol/string agnostic)' do
_(Stripe::Coupons[:gold25]).must_equal Stripe::Coupons::GOLD25
_(Stripe::Coupons['gold25']).must_equal Stripe::Coupons::GOLD25
end
describe 'uploading' do
describe 'when none exists on stripe.com' do
before do
Stripe::Coupon.stubs(:retrieve).raises(Stripe::InvalidRequestError.new("not found", "id"))
end
it 'creates the plan online' do
Stripe::Coupon.expects(:create).with(
:id => :gold25,
:duration => 'repeating',
:duration_in_months => 10,
:amount_off => 100,
:currency => 'USD',
:max_redemptions => 3,
:percent_off => 25,
:redeem_by => @now
)
Stripe::Coupons.put!
end
end
describe 'when it is already present on stripe.com' do
before do
Stripe::Coupon.stubs(:retrieve).returns(Stripe::Coupon.construct_from({
:id => :gold25,
}))
end
it 'is a no-op' do
Stripe::Coupon.expects(:create).never
Stripe::Coupons.put!
end
end
end
describe 'reseting' do
describe 'when it does not exist on stripe.com'do
before do
Stripe::Coupon.stubs(:retrieve).raises(Stripe::InvalidRequestError.new("not found", "id"))
end
it 'creates the plan' do
Stripe::Coupon.expects(:create).with(
:id => :gold25,
:duration => 'repeating',
:duration_in_months => 10,
:amount_off => 100,
:currency => 'USD',
:max_redemptions => 3,
:percent_off => 25,
:redeem_by => @now
)
Stripe::Coupons.reset!
end
end
describe 'when it does exist on stripe.com already' do
before do
@coupon = Stripe::Coupon.construct_from({
:id => :gold25,
})
Stripe::Coupon.stubs(:retrieve).returns(@coupon)
end
it 'first deletes the coupon then re-adds it' do
@coupon.expects(:delete)
Stripe::Coupon.expects(:create).with(
:id => :gold25,
:duration => 'repeating',
:duration_in_months => 10,
:amount_off => 100,
:currency => 'USD',
:max_redemptions => 3,
:percent_off => 25,
:redeem_by => @now
)
Stripe::Coupons.reset!
end
end
end
end
describe 'with missing mandatory values' do
it 'raises an exception after configuring it' do
_(proc {Stripe.coupon(:bad) {}}).must_raise Stripe::InvalidConfigurationError
end
end
end
================================================
FILE: test/dummy/README.rdoc
================================================
== Welcome to Rails
Rails is a web-application framework that includes everything needed to create
database-backed web applications according to the Model-View-Control pattern.
This pattern splits the view (also called the presentation) into "dumb"
templates that are primarily responsible for inserting pre-built data in between
HTML tags. The model contains the "smart" domain objects (such as Account,
Product, Person, Post) that holds all the business logic and knows how to
persist themselves to a database. The controller handles the incoming requests
(such as Save New Account, Update Product, Show Post) by manipulating the model
and directing data to the view.
In Rails, the model is handled by what's called an object-relational mapping
layer entitled Active Record. This layer allows you to present the data from
database rows as objects and embellish these data objects with business logic
methods. You can read more about Active Record in
link:files/vendor/rails/activerecord/README.html.
The controller and view are handled by the Action Pack, which handles both
layers by its two parts: Action View and Action Controller. These two layers
are bundled in a single package due to their heavy interdependence. This is
unlike the relationship between the Active Record and Action Pack that is much
more separate. Each of these packages can be used independently outside of
Rails. You can read more about Action Pack in
link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. At the command prompt, create a new Rails application:
<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
2. Change directory to <tt>myapp</tt> and start the web server:
<tt>cd myapp; rails server</tt> (run with --help for options)
3. Go to http://localhost:3000/ and you'll see:
"Welcome aboard: You're riding Ruby on Rails!"
4. Follow the guidelines to start developing your application. You can find
the following resources handy:
* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
== Debugging Rails
Sometimes your application goes wrong. Fortunately there are a lot of tools that
will help you debug it and get it back on the rails.
First area to check is the application log files. Have "tail -f" commands
running on the server.log and development.log. Rails will automatically display
debugging and runtime information to these files. Debugging info will also be
shown in the browser on requests from 127.0.0.1.
You can also log your own messages directly into the log file from your code
using the Ruby logger class from inside your controllers. Example:
class WeblogController < ActionController::Base
def destroy
@weblog = Weblog.find(params[:id])
@weblog.destroy
logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
end
end
The result will be a message in your log file along the lines of:
Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
More information on how to use the logger is at http://www.ruby-doc.org/core/
Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
several books available online as well:
* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
These two books will bring you up to speed on the Ruby language and also on
programming in general.
== Debugger
Debugger support is available through the debugger command when you start your
Mongrel or WEBrick server with --debugger. This means that you can break out of
execution at any point in the code, investigate and change the model, and then,
resume execution! You need to install ruby-debug to run the server in debugging
mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
class WeblogController < ActionController::Base
def index
@posts = Post.all
debugger
end
end
So the controller will accept the action, run the first line, then present you
with a IRB prompt in the server window. Here you can do things like:
>> @posts.inspect
=> "[#<Post:0x14a6be8
@attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
#<Post:0x14a6620
@attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
>> @posts.first.title = "hello from a debugger"
=> "hello from a debugger"
...and even better, you can examine how your runtime objects actually work:
>> f = @posts.first
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
>> f.
Display all 152 possibilities? (y or n)
Finally, when you're ready to resume execution, you can enter "cont".
== Console
The console is a Ruby shell, which allows you to interact with your
application's domain model. Here you'll have all parts of the application
configured, just like it is when the application is running. You can inspect
domain models, change values, and save to the database. Starting the script
without arguments will launch it in the development environment.
To start the console, run <tt>rails console</tt> from the application
directory.
Options:
* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
made to the database.
* Passing an environment name as an argument will load the corresponding
environment. Example: <tt>rails console production</tt>.
To reload your controllers and models after launching the console run
<tt>reload!</tt>
More information about irb can be found at:
link:http://www.rubycentral.org/pickaxe/irb.html
== dbconsole
You can go to the command line of your database directly through <tt>rails
dbconsole</tt>. You would be connected to the database with the credentials
defined in database.yml. Starting the script without arguments will connect you
to the development database. Passing an argument will connect you to a different
database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
PostgreSQL and SQLite 3.
== Description of Contents
The default directory structure of a generated Ruby on Rails application:
|-- app
| |-- assets
| |-- images
| |-- javascripts
| `-- stylesheets
| |-- controllers
| |-- helpers
| |-- mailers
| |-- models
| `-- views
| `-- layouts
|-- config
| |-- environments
| |-- initializers
| `-- locales
|-- db
|-- doc
|-- lib
| `-- tasks
|-- log
|-- public
|-- script
|-- test
| |-- fixtures
| |-- functional
| |-- integration
| |-- performance
| `-- unit
|-- tmp
| |-- cache
| |-- pids
| |-- sessions
| `-- sockets
`-- vendor
|-- assets
`-- stylesheets
`-- plugins
app
Holds all the code that's specific to this particular application.
app/assets
Contains subdirectories for images, stylesheets, and JavaScript files.
app/controllers
Holds controllers that should be named like weblogs_controller.rb for
automated URL mapping. All controllers should descend from
ApplicationController which itself descends from ActionController::Base.
app/models
Holds models that should be named like post.rb. Models descend from
ActiveRecord::Base by default.
app/views
Holds the template files for the view that should be named like
weblogs/index.html.erb for the WeblogsController#index action. All views use
eRuby syntax by default.
app/views/layouts
Holds the template files for layouts to be used with views. This models the
common header/footer method of wrapping views. In your views, define a layout
using the <tt>layout :default</tt> and create a file named default.html.erb.
Inside default.html.erb, call <% yield %> to render the view using this
layout.
app/helpers
Holds view helpers that should be named like weblogs_helper.rb. These are
generated for you automatically when using generators for controllers.
Helpers can be used to wrap functionality for your views into methods.
config
Configuration files for the Rails environment, the routing map, the database,
and other dependencies.
db
Contains the database schema in schema.rb. db/migrate contains all the
sequence of Migrations for your schema.
doc
This directory is where your application documentation will be stored when
generated using <tt>rake doc:app</tt>
lib
Application specific libraries. Basically, any kind of custom code that
doesn't belong under controllers, models, or helpers. This directory is in
the load path.
public
The directory available for the web server. Also contains the dispatchers and the
default HTML files. This should be set as the DOCUMENT_ROOT of your web
server.
script
Helper scripts for automation and generation.
test
Unit and functional tests along with fixtures. When using the rails generate
command, template test files will be generated for you and placed in this
directory.
vendor
External libraries that the application depends on. Also includes the plugins
subdirectory. If the app has frozen rails, those gems also go here, under
vendor/rails/. This directory is in the load path.
================================================
FILE: test/dummy/Rakefile
================================================
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
Dummy::Application.load_tasks
================================================
FILE: test/dummy/app/assets/javascripts/application.js
================================================
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
================================================
FILE: test/dummy/app/assets/stylesheets/application.css
================================================
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require_tree .
*/
================================================
FILE: test/dummy/app/controllers/apis_controller.rb
================================================
ApiControllerKlass = defined?(ActionController::API) ? ActionController::API : ApplicationController
class ApisController < ApiControllerKlass
def index
render json: :ok
end
end
================================================
FILE: test/dummy/app/controllers/application_controller.rb
================================================
class ApplicationController < ActionController::Base
protect_from_forgery
end
================================================
FILE: test/dummy/app/controllers/stripes_controller.rb
================================================
class StripesController < ApplicationController
def new
end
end
================================================
FILE: test/dummy/app/helpers/application_helper.rb
================================================
module ApplicationHelper
end
================================================
FILE: test/dummy/app/mailers/.gitkeep
================================================
================================================
FILE: test/dummy/app/models/.gitkeep
================================================
================================================
FILE: test/dummy/app/models/dummy/model_with_callbacks.rb
================================================
class Dummy::ModelWithCallbacks
end
================================================
FILE: test/dummy/app/views/layouts/application.html.erb
================================================
<!DOCTYPE html>
<html>
<head>
<title>Dummy</title>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
================================================
FILE: test/dummy/app/views/stripes/new.html.erb
================================================
<%= stripe_javascript_tag (params[:version] || :v3) %>
<script>
if (typeof(Stripe) != 'undefined') {
document.write("<p>This page tests the loading and initialization of Stripe JS</p>");
}
</script>
================================================
FILE: test/dummy/config/application.rb
================================================
require File.expand_path('../boot', __FILE__)
require 'rails'
require 'action_controller/railtie'
require 'rails/test_unit/railtie'
Bundler.require
require "stripe-rails"
module Dummy
class Application < Rails::Application
config.stripe.publishable_key = 'pk_test_XXXYYYZZZ'
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Enforce whitelist mode for mass assignment.
# This will create an empty whitelist of attributes available for mass-assignment for all models
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
# Enable the asset pipeline
#config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
#config.assets.version = '1.0'
end
end
================================================
FILE: test/dummy/config/boot.rb
================================================
require 'rubygems'
gemfile = File.expand_path('../../../../Gemfile', __FILE__)
if File.exist?(gemfile)
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
end
$:.unshift File.expand_path('../../../../lib', __FILE__)
================================================
FILE: test/dummy/config/database.yml
================================================
# SQLite version 3.x
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: sqlite3
database: db/test.sqlite3
pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
pool: 5
timeout: 5000
================================================
FILE: test/dummy/config/environment.rb
================================================
# Load the rails application
require File.expand_path('../application', __FILE__)
# Initialize the rails application
Dummy::Application.initialize!
================================================
FILE: test/dummy/config/environments/development.rb
================================================
Dummy::Application.configure do
config.eager_load = false
# Settings specified here will take precedence over those in config/application.rb
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Log error messages when you accidentally call methods on nil.
config.whiny_nils = true
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Don't care if the mailer can't send
# config.action_mailer.raise_delivery_errors = false
# Print deprecation notices to the Rails logger
config.active_support.deprecation = :log
# Only use best-standards-support built into browsers
config.action_dispatch.best_standards_support = :builtin
# Raise exception on mass assignment protection for Active Record models
# config.active_record.mass_assignment_sanitizer = :strict
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
# config.active_record.auto_explain_threshold_in_seconds = 0.5
# Do not compress assets
# config.assets.compress = false
# Expands the lines which load the assets
# config.assets.debug = true
end
================================================
FILE: test/dummy/config/environments/production.rb
================================================
Dummy::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# Code is not reloaded between requests
config.cache_classes = true
# Full error reports are disabled and caching is turned on
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = false
# Compress JavaScripts and CSS
config.assets.compress = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Defaults to nil and saved in location specified by config.assets.prefix
# config.assets.manifest = YOUR_PATH
# Specifies the header that your server uses for sending files
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# See everything in the log (default is :info)
# config.log_level = :debug
# Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
# Enable threaded mode
# config.threadsafe!
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
# config.active_record.auto_explain_threshold_in_seconds = 0.5
end
================================================
FILE: test/dummy/config/environments/test.rb
================================================
Dummy::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_assets = true
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
#config.action_mailer.delivery_method = :test
# Raise exception on mass assignment protection for Active Record models
#config.active_record.mass_assignment_sanitizer = :strict
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
# Set eager_load to false as it's unneeded
config.eager_load = false
end
================================================
FILE: test/dummy/config/initializers/backtrace_silencers.rb
================================================
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
# Rails.backtrace_cleaner.remove_silencers!
================================================
FILE: test/dummy/config/initializers/inflections.rb
================================================
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format
# (all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
#
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections do |inflect|
# inflect.acronym 'RESTful'
# end
================================================
FILE: test/dummy/config/initializers/mime_types.rb
================================================
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
# Mime::Type.register_alias "text/html", :iphone
================================================
FILE: test/dummy/config/initializers/secret_token.rb
================================================
# Be sure to restart your server when you modify this file.
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Dummy::Application.config.secret_token = '5e507bc08a89b8b07e1ce08c932ec89771be6500e4d2d64c38164d2405d27578c33edc2982dd9ee67d4c49e0dd75992ac2ed6eb120bea0e16516549466906d06'
Dummy::Application.config.secret_key_base = 'a87ae91f28326ebe41f7ce9b7b799bb9'
================================================
FILE: test/dummy/config/initializers/session_store.rb
================================================
# Be sure to restart your server when you modify this file.
Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails generate session_migration")
# Dummy::Application.config.session_store :active_record_store
================================================
FILE: test/dummy/config/initializers/wrap_parameters.rb
================================================
# Be sure to restart your server when you modify this file.
#
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
# Disable root element in JSON by default.
ActiveSupport.on_load(:active_record) do
self.include_root_in_json = false
end
================================================
FILE: test/dummy/config/locales/en.yml
================================================
# Sample localization file for English. Add more files in this directory for other locales.
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
hello: "Hello world"
================================================
FILE: test/dummy/config/routes.rb
================================================
Dummy::Application.routes.draw do
resources :stripes, only: :new
resources :apis, only: :index
# The priority is based upon order of creation:
# first created -> highest priority.
# Sample of regular route:
# match 'products/:id' => 'catalog#view'
# Keep in mind you can assign values other than :controller and :action
# Sample of named route:
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
# This route can be invoked with purchase_url(:id => product.id)
# Sample resource route (maps HTTP verbs to controller actions automatically):
# resources :products
# Sample resource route with options:
# resources :products do
# member do
# get 'short'
# post 'toggle'
# end
#
# collection do
# get 'sold'
# end
# end
# Sample resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
# Sample resource route with more complex sub-resources
# resources :products do
# resources :comments
# resources :sales do
# get 'recent', :on => :collection
# end
# end
# Sample resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
# root :to => 'welcome#index'
# See how all your routes lay out with "rake routes"
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
# match ':controller(/:action(/:id))(.:format)'
end
================================================
FILE: test/dummy/config/stripe/plans.rb
================================================
Stripe.plan :gold do |plan|
plan.name = 'Solid Gold'
plan.amount = 699
plan.interval = 'month'
end
Stripe.plan "Solid Gold".to_sym do |plan|
plan.constant_name = 'SOLID_GOLD'
plan.name = 'Solid Gold'
plan.amount = 699
plan.interval = 'month'
end
Stripe.plan :alternative_currency do |plan|
plan.name = 'Alternative Currency'
plan.amount = 699
plan.interval = 'month'
plan.currency = 'cad'
end
Stripe.plan :metered do |plan|
plan.name = 'Metered'
plan.amount = 699
plan.interval = 'month'
plan.usage_type = 'metered'
plan.aggregate_usage = 'max'
plan.billing_scheme = 'per_unit'
end
Stripe.plan :tiered do |plan|
plan.name = 'Tiered'
plan.aggregate_usage = 'max'
plan.billing_scheme = 'tiered'
# interval must be either 'day', 'week', 'month' or 'year'
plan.interval = 'month'
plan.interval_count = 1
plan.tiers = [
{
unit_amount: 1500,
up_to: 10
},
{
unit_amount: 1000,
up_to: 'inf'
}
]
plan.tiers_mode = 'graduated'
plan.usage_type = 'metered'
end
================================================
FILE: test/dummy/config/stripe/prices.rb
================================================
Stripe.price :gold do |price|
price.name = 'Solid Gold'
price.unit_amount = 699
price.recurring = {
interval: 'month'
}
end
Stripe.price :taxable_gold do |price|
price.name = 'Taxable Gold'
price.unit_amount = 699
price.tax_behavior = 'exclusive'
end
Stripe.price "Solid Gold".to_sym do |price|
price.constant_name = 'SOLID_GOLD'
price.name = 'Solid Gold'
price.unit_amount = 699
price.recurring = {
interval: 'month'
}
end
Stripe.price :alternative_currency do |price|
price.name = 'Alternative Currency'
price.unit_amount = 699
price.recurring = {
interval: 'month'
}
price.currency = 'cad'
end
Stripe.price :metered do |price|
price.name = 'Metered'
price.unit_amount = 699
price.recurring = {
interval: 'month',
aggregate_usage: 'max',
usage_type: 'metered'
}
price.billing_scheme = 'per_unit'
end
Stripe.price :tiered do |price|
price.name = 'Tiered'
price.billing_scheme = 'tiered'
# interval must be either 'day', 'week', 'month' or 'year'
price.recurring = {
interval: 'month',
interval_count: 2,
aggregate_usage: 'max',
usage_type: 'metered'
}
price.tiers = [
{
unit_amount: 1500,
up_to: 10
},
{
unit_amount: 1000,
up_to: 'inf'
}
]
price.tiers_mode = 'graduated'
end
================================================
FILE: test/dummy/config.ru
================================================
# This file is used by Rack-based servers to start the application.
require ::File.expand_path('../config/environment', __FILE__)
run Dummy::Application
================================================
FILE: test/dummy/lib/assets/.gitkeep
================================================
================================================
FILE: test/dummy/lib/dummy/module_with_callbacks.rb
================================================
class Dummy::ModuleWithCallbacks
end
================================================
FILE: test/dummy/log/.gitkeep
================================================
================================================
FILE: test/dummy/public/404.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>The page you were looking for doesn't exist (404)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/404.html -->
<div class="dialog">
<h1>The page you were looking for doesn't exist.</h1>
<p>You may have mistyped the address or the page may have moved.</p>
</div>
</body>
</html>
================================================
FILE: test/dummy/public/422.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>The change you wanted was rejected (422)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/422.html -->
<div class="dialog">
<h1>The change you wanted was rejected.</h1>
<p>Maybe you tried to change something you didn't have access to.</p>
</div>
</body>
</html>
================================================
FILE: test/dummy/public/500.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>We're sorry, but something went wrong (500)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
<!-- This file lives in public/500.html -->
<div class="dialog">
<h1>We're sorry, but something went wrong.</h1>
</div>
</body>
</html>
================================================
FILE: test/dummy/script/rails
================================================
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
================================================
FILE: test/dummy_apis_controller_spec.rb
================================================
require 'spec_helper'
describe ApisController do
include Rack::Test::Methods
let(:app) { Rails.application }
before do
header 'Accept', 'application/json'
header 'Content-Type', 'application/json'
end
describe 'the apis interface' do
subject { get '/apis/' }
it { _(subject).must_be :ok? }
end
end
================================================
FILE: test/dummy_stripes_controller_spec.rb
================================================
require 'spec_helper'
class DummyStripesControllerSpec < ApplicationSystemTestCase
setup do
Dummy::Application.configure do
config.stripe.publishable_key = 'pk_test_XXXYYYZZZ'
end
end
test "loading the default javascript helper" do
visit new_stripe_url
assert_text 'This page tests the loading and initialization of Stripe JS'
end
test "loading the v2 version of the javascript helper" do
visit new_stripe_url(version: 'v2')
assert_text 'This page tests the loading and initialization of Stripe JS'
end
end
================================================
FILE: test/event.json
================================================
{
"api_version": "2017-02-14",
"created": 1234567890,
"customer_email": "",
"data": {
"object": {
}
},
"id": "evt_19tLKfDSlTMT26MkKD3pohqX",
"livemode": false,
"object": "event",
"pending_webhooks": 0,
"recipient_best_description": "",
"request": {
"id": "req_ArJ9y3DcEShbw2",
"idempotency_key": null
},
"type": "invoice.payment_succeeded"
}
================================================
FILE: test/events_controller_spec.rb
================================================
require 'spec_helper'
describe Stripe::EventsController do
include Rack::Test::Methods
let(:app) { Rails.application }
before do
header 'Accept', 'application/json'
header 'Content-Type', 'application/json'
end
describe 'the events interface' do
subject { post '/stripe/events', params.to_json }
before { stripe_events_stub }
let(:params) {
{
id: 'evt_00000000000000',
type: 'customer.updated',
data: {object: 'customer'},
}
}
let(:stripe_events_stub) do
stub_request(:get, "https://api.stripe.com/v1/events/evt_00000000000000").
to_return(status: 200, body: Stripe::Event.construct_from(params).to_json, headers: {})
end
it { _(subject).must_be :ok? }
it 'should call the stripe_events_stub' do
subject
assert_requested(stripe_events_stub)
end
describe 'when signing_secret is nil' do
before do
header 'Stripe-Signature', 't=1537832721,v1=123,v0=123'
app.config.stripe.signing_secret = nil
end
it 'should call the stripe_events_stub' do
subject
assert_requested(stripe_events_stub)
end
end
end
describe 'signed webhooks' do
before do
header 'Stripe-Signature', 't=1537832721,v1=123,v0=123'
app.config.stripe.signing_secret = 'SECRET'
end
after { app.config.stripe.signing_secret = nil }
let(:params) {
{
id: 'evt_00000000000001',
type: 'customer.updated',
data: {
object: 'customer',
fingerprint: 'xxxyyyzzz'
},
}
}
subject { post '/stripe/events', params.to_json }
it 'returns bad_request when invalid' do
Stripe::Webhook.expects(:construct_event).raises(Stripe::SignatureVerificationError.new('msg', 'sig_header'))
_(subject).must_be :bad_request?
end
it 'returns ok when valid' do
Stripe::Webhook.expects(:construct_event).returns(Stripe::Event.construct_from(params))
_(subject).must_be :ok?
end
end
describe 'multiple signed webhooks' do
before do
header 'Stripe-Signature', 't=1537832721,v1=123,v0=123'
app.config.stripe.signing_secrets = ['SECRET1', 'SECRET2']
end
after { app.config.stripe.signing_secrets = nil }
let(:params) {
{
id: 'evt_00000000000001',
type: 'customer.updated',
data: {
object: 'customer',
fingerprint: 'xxxyyyzzz'
},
}
}
subject { post '/stripe/events', params.to_json }
it 'returns bad_request when invalid' do
Stripe::Webhook.expects(:construct_event).twice.raises(Stripe::SignatureVerificationError.new('msg', 'sig_header'))
_(subject).must_be :bad_request?
end
it 'returns ok when valid' do
Stripe::Webhook.expects(:construct_event).returns(Stripe::Event.construct_from(params))
_(subject).must_be :ok?
end
end
end
================================================
FILE: test/fixtures/stripe_plans.json
================================================
{
"object": "list",
"count": 0,
"data": [],
"has_more": false,
"url": "/v1/plans"
}
================================================
FILE: test/fixtures/stripe_plans_headers.json
================================================
{
"Server": "nginx",
"Date": "Wed, 21 Mar 2018 20:04:56 GMT",
"Content-Type": "application/json",
"Content-Length": "5807",
"Connection": "close",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS, DELETE",
"Access-Control-Allow-Origin": "*",
"Access-Control-Expose-Headers": "Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required",
"Access-Control-Max-Age": "300",
"Cache-Control": "no-cache, no-store",
"Request-Id": "req_UCuedZo4sKqUXn",
"Stripe-Version": "2018-02-05",
"Strict-Transport-Security": "max-age=31556926; includeSubDomains; preload"
}
================================================
FILE: test/fixtures/stripe_plans_headers_2017.json
================================================
{
"Server": "nginx",
"Date": "Wed, 21 Mar 2018 20:04:56 GMT",
"Content-Type": "application/json",
"Content-Length": "5807",
"Connection": "close",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS, DELETE",
"Access-Control-Allow-Origin": "*",
"Access-Control-Expose-Headers": "Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required",
"Access-Control-Max-Age": "300",
"Cache-Control": "no-cache, no-store",
"Request-Id": "req_UCuedZo4sKqUXn",
"Stripe-Version": "2017-02-05",
"Strict-Transport-Security": "max-age=31556926; includeSubDomains; preload"
}
================================================
FILE: test/fixtures/stripe_prices.json
================================================
{
"object": "list",
"count": 0,
"data": [],
"has_more": false,
"url": "/v1/prices"
}
================================================
FILE: test/invoice.json
================================================
{
"amount_due": 0,
"application_fee": 0,
"attempt_count": 0,
"attempted": true,
"billing": "",
"charge": "",
"closed": true,
"currency": "usd",
"customer": "cus_ADmuABetLS15eF",
"date": 1234567890,
"description": "",
"discount": {
},
"due_date": 1234567890,
"ending_balance": 0,
"forgiven": false,
"id": "in_19zuuiDSlTMT26Mk1XitxqCb",
"lines": {
"data": [
{
"amount": 2000,
"currency": "usd",
"description": null,
"discountable": true,
"id": "sub_AKa4uss8vCMAZC",
"livemode": true,
"metadata": {
},
"object": "line_item",
"period": {
"end": 1495403592,
"start": 1492811592
},
"plan": {
"amount": 2000,
"created": 1488566449,
"currency": "usd",
"id": "gold",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {
},
"name": "Gold Special",
"object": "plan",
"statement_descriptor": null,
"trial_period_days": null
},
"proration": false,
"quantity": 1,
"subscription": null,
"subscription_item": "si_19zuuiDSlTMT26Mk9HayBPe9",
"type": "subscription"
}
],
"object": "list",
"total_count": 1,
"url": "/v1/invoices/in_19zuuiDSlTMT26Mk1XitxqCb/lines"
},
"livemode": false,
"metadata": {
},
"next_payment_attempt": 1234567890,
"number": "",
"object": "invoice",
"paid": true,
"period_end": 1234567890,
"period_start": 1234567890,
"receipt_number": "",
"send_next_at": 1234567890,
"starting_balance": 0,
"statement_descriptor": "",
"subscription": "",
"subscription_proration_date": 0,
"subtotal": 0,
"tax": 0,
"tax_percent": 0.0,
"total": 6999,
"webhooks_delivered_at": 1234567890
}
================================================
FILE: test/javascript_helper_spec.rb
================================================
require 'spec_helper'
describe Stripe::JavascriptHelper do
before { Rails.application.config.stripe.publishable_key = 'pub_xxxx' }
let(:controller) { ActionView::TestCase::TestController.new }
let(:view) { controller.view_context }
describe '#stripe_javascript_tag' do
describe 'when no options are passed' do
it 'should default to v3' do
_(view.stripe_javascript_tag).must_include 'https://js.stripe.com/v3/'
end
end
describe 'when the v2 option is passed' do
it 'should default to v2' do
_(view.stripe_javascript_tag(:v2)).must_include 'https://js.stripe.com/v2/'
end
end
describe 'when the debug flag is enabled' do
before { Rails.application.config.stripe.debug_js = true }
after { Rails.application.config.stripe.debug_js = false }
it 'should render the debug js' do
_(view.stripe_javascript_tag(:v1)).must_include 'https://js.stripe.com/v1/stripe-debug.js'
end
describe 'when v3 is selected' do
it 'should not render debug js' do
_(view.stripe_javascript_tag(:v3)).wont_include 'https://js.stripe.com/v1/stripe-debug.js'
end
end
end
end
describe "render :partial => 'stripe/js'" do
subject { view.render :partial => 'stripe/js' }
it 'should render correctly' do
_(subject).must_include 'https://js.stripe.com/v3/'
end
end
describe "render :partial => 'stripe/js', local: {stripe_js_version: 'v2'}" do
subject { view.render :partial => 'stripe/js', locals: {stripe_js_version: 'v2'} }
it 'should render correctly' do
_(subject).must_include 'https://js.stripe.com/v2/'
end
end
describe '#stripe_elements_tag' do
describe 'when no options are passed' do
it 'should display the form' do
_(view.stripe_elements_tag(
submit_path: '/charge',
)).must_include 'Credit or debit card'
end
end
describe 'with options' do
describe 'without default js' do
it 'wont include the default script tag' do
_(view.stripe_elements_tag(
submit_path: '/charge',
js_path: 'another/path'
)).wont_include '<script id="stripe_elements_js">'
end
end
describe 'without default css' do
it 'wont include the default style tag' do
_(view.stripe_elements_tag(
submit_path: '/charge',
css_path: 'another/path'
)).wont_include '<style>'
end
end
end
describe 'with block' do
let(:markup) { '<input type="text" />'.html_safe }
it 'should display block contents' do
block = lambda { markup }
result = view.stripe_elements_tag(submit_path: '/charge', &block)
assert_match %r%<input type="text" />%, result
end
end
end
end
================================================
FILE: test/plan_builder_spec.rb
================================================
require 'spec_helper'
describe 'building plans' do
describe 'simply' do
before do
Stripe.plan :primo do |plan|
plan.name = 'Acme as a service PRIMO'
plan.amount = 699
plan.interval = 'month'
plan.interval_count = 3
plan.trial_period_days = 30
plan.metadata = {:number_of_awesome_things => 5}
plan.statement_descriptor = 'Acme Primo'
plan.active = true
plan.nickname = 'primo'
plan.usage_type = 'metered'
plan.billing_scheme = 'per_unit'
plan.aggregate_usage = 'sum'
plan.tiers_mode = 'graduated'
end
end
after { Stripe::Plans.send(:remove_const, :PRIMO) }
it 'is accessible via id' do
_(Stripe::Plans::PRIMO).wont_be_nil
end
it 'is accessible via collection' do
_(Stripe::Plans.all).must_include Stripe::Plans::PRIMO
end
it 'is accessible via hash lookup (symbol/string agnostic)' do
_(Stripe::Plans[:primo]).must_equal Stripe::Plans::PRIMO
_(Stripe::Plans['primo']).must_equal Stripe::Plans::PRIMO
end
it 'accepts a billing interval of a day' do
Stripe.plan :daily do |plan|
plan.name = 'Acme as a service daily'
plan.amount = 100
plan.interval = 'day'
end
_(Stripe::Plans::DAILY).wont_be_nil
end
it 'accepts a billing interval of a week' do
Stripe.plan :weekly do |plan|
plan.name = 'Acme as a service weekly'
plan.amount = 100
plan.interval = 'week'
end
_(Stripe::Plans::WEEKLY).wont_be_nil
end
it 'accepts a billing interval of a month' do
Stripe.plan :monthly do |plan|
plan.name = 'Acme as a service monthly'
plan.amount = 400
plan.interval = 'month'
end
_(Stripe::Plans::MONTHLY).wont_be_nil
end
it 'accepts a billing interval of a year' do
Stripe.plan :yearly do |plan|
plan.name = 'Acme as a service yearly'
plan.amount = 4800
plan.interval = 'year'
end
_(Stripe::Plans::YEARLY).wont_be_nil
end
it 'denies arbitrary billing intervals' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service BROKEN'
plan.amount = 999
plan.interval = 'anything'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'accepts a statement descriptor' do
Stripe.plan :described do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.statement_descriptor = 'ACME Monthly'
end
_(Stripe::Plans::DESCRIBED).wont_be_nil
end
it 'denies statement descriptors that are too long' do
_(lambda {
Stripe.plan :described do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.statement_descriptor = 'ACME as a Service Monthly'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'denies invalid values for active' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.active = 'whatever'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'denies invalid values for usage_type' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.usage_type = 'whatever'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'denies invalid values for aggregate_usage' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.aggregate_usage = 'whatever'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'denies aggregate_usage if usage type is licensed' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.usage_type = 'licensed'
plan.aggregate_usage = 'sum'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'denies invalid values for billing_scheme' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.billing_scheme = 'whatever'
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'denies invalid values for tiers_mode' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.amount = 999
plan.interval = 'month'
plan.tiers_mode = 'whatever'
end
}).must_raise Stripe::InvalidConfigurationError
end
describe 'name and product id validation' do
it 'should be valid when using just the product id' do
Stripe.plan :prodded do |plan|
plan.product_id = 'acme'
plan.amount = 999
plan.interval = 'month'
end
_(Stripe::Plans::PRODDED).wont_be_nil
end
it 'should be invalid when using both name and product id' do
_(lambda {
Stripe.plan :broken do |plan|
plan.name = 'Acme as a service'
plan.product_id = 'acme'
plan.amount = 999
plan.interval = 'month'
end
}).must_raise Stripe::InvalidConfigurationError
end
end
describe 'uploading' do
include FixtureLoader
describe 'when none exists on stripe.com' do
let(:headers) { load_request_fixture('stripe_plans_headers_2017.json') }
before do
Stripe.api_version = '2018-02-04'
Stripe::Plan.stubs(:retrieve).raises(Stripe::InvalidRequestError.new("not found", "id"))
stub_request(:get, "https://api.stripe.com/v1/plans").
with(headers: { 'Authorization'=>'Bearer XYZ',}).
to_return(status: 200, body: load_request_fixture('stripe_plans.json'), headers: JSON.parse(headers))
end
after { Stripe.api_version = nil }
it 'creates the plan online' do
Stripe::Plan.expects(:create).with(
:id => :gold,
:currency => 'usd',
:name => 'Solid Gold',
:amount => 699,
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0
)
Stripe::Plans::GOLD.put!
end
it 'creates a plan with an alternative currency' do
Stripe::Plan.expects(:create).with(
:id => :alternative_currency,
:currency => 'cad',
:name => 'Alternative Currency',
:amount => 699,
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0
)
Stripe::Plans::ALTERNATIVE_CURRENCY.put!
end
describe 'when using the API version that supports products' do
before { Stripe.api_version = '2018-02-05' }
after { Stripe.api_version = nil }
it 'creates the plan online' do
Stripe::Plan.expects(:create).with(
:id => :gold,
:currency => 'usd',
:product => {
:name => 'Solid Gold',
:statement_descriptor => nil,
},
:amount => 699,
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0
)
Stripe::Plans::GOLD.put!
end
it 'creates a metered plan' do
Stripe::Plan.expects(:create).with(
:id => :metered,
:currency => 'usd',
:product => {
:name => 'Metered',
:statement_descriptor => nil,
},
:amount => 699,
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0,
:usage_type => 'metered',
:aggregate_usage => 'max',
:billing_scheme => 'per_unit'
)
Stripe::Plans::METERED.put!
end
it 'creates a tiered plan' do
Stripe::Plan.expects(:create).with(
:id => :tiered,
:currency => 'usd',
:product => {
:name => 'Tiered',
:statement_descriptor => nil,
},
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0,
:usage_type => 'metered',
:aggregate_usage => 'max',
:billing_scheme => 'tiered',
:tiers => [
{
:unit_amount => 1500,
:up_to => 10
},
{
:unit_amount => 1000,
:up_to => 'inf'
}
],
:tiers_mode => 'graduated'
)
plan = Stripe::Plans::TIERED
Stripe::Plans::TIERED.put!
end
describe 'when passed invalid arguments for tiered pricing' do
it 'raises a Stripe::InvalidConfigurationError when billing tiers are invalid' do
_(lambda {
Stripe.plan "Bad Tiers".to_sym do |plan|
plan.name = 'Acme as a service BAD TIERS'
plan.constant_name = 'BAD_TIERS'
plan.interval = 'month'
plan.interval_count = 1
plan.trial_period_days = 30
plan.usage_type = 'metered'
plan.tiers_mode = 'graduated'
plan.billing_scheme = 'per_unit'
plan.aggregate_usage = 'sum'
plan.tiers = [
{
unit_amount: 1500,
up_to: 10
},
{
unit_amount: 1000,
}
]
end
}).must_raise Stripe::InvalidConfigurationError
end
it 'raises a Stripe::InvalidConfigurationError when billing tiers is not an array' do
_(lambda {
Stripe.plan "Bad Tiers".to_sym do |plan|
plan.name = 'Acme as a service BAD TIERS'
plan.constant_name = 'BAD_TIERS'
plan.interval = 'month'
plan.interval_count = 1
plan.trial_period_days = 30
plan.usage_type = 'metered'
plan.tiers_mode = 'graduated'
plan.billing_scheme = 'per_unit'
plan.aggregate_usage = 'sum'
plan.tiers = {
unit_amount: 1500,
up_to: 10
}
end
}).must_raise Stripe::InvalidConfigurationError
end
end
describe 'when using a product id' do
before do
Stripe::Plans::GOLD.product_id = 'prod_XXXXXXXXXXXXXX'
Stripe::Plans::GOLD.name = nil
end
after do
Stripe::Plans::GOLD.product_id = nil
Stripe::Plans::GOLD.name = 'Solid Gold'
end
it 'creates the plan online with the product id' do
Stripe::Plan.expects(:create).with(
:id => :gold,
:currency => 'usd',
:product => 'prod_XXXXXXXXXXXXXX',
:amount => 699,
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0
)
Stripe::Plans::GOLD.put!
end
end
end
describe 'when api_version is not set for api versions that support products' do
before { Stripe.api_version = nil }
subject { Stripe::Plans::GOLD.put! }
let(:headers) { load_request_fixture('stripe_plans_headers.json') }
it 'creates the plan online' do
Stripe::Plan.expects(:create).with(
:id => :gold,
:currency => 'usd',
:product => {
:name => 'Solid Gold',
:statement_descriptor => nil,
},
:amount => 699,
:interval => 'month',
:interval_count => 1,
:trial_period_days => 0
)
subject
end
end
end
describe 'when it is already present on stripe.com' do
before do
Stripe::Plan.stubs(:retrieve).returns(Stripe::Plan.construct_from({
:id => :gold,
:name => 'Solid Gold'
}))
end
it 'is a no-op' do
Stripe::Plan.expects(:create).never
Stripe::Plans::GOLD.put!
end
end
end
end
describe 'with missing mandatory values' do
it 'raises an exception after configuring it' do
_(-> { Stripe.plan(:bad) {} }).must_raise Stripe::InvalidConfigurationError
end
end
describe 'with custom constant name' do
before do
Stripe.plan "Primo Plan".to_sym do |plan|
plan.name = 'Acme as a service PRIMO'
plan.constant_name = 'PRIMO_PLAN'
plan.amount = 699
plan.interval = 'month'
plan.interval_count = 3
plan.trial_period_days = 30
plan.metadata = {:number_of_awesome_things => 5}
plan.statement_descriptor = 'Acme Primo'
plan.active = true
plan.nickname = 'primo'
plan.usage_type = 'metered'
plan.billing_scheme = 'per_unit'
plan.aggregate_usage = 'sum'
plan.tiers_mode = 'graduated'
end
end
after { Stripe::Plans.send(:remove_const, :PRIMO_PLAN) }
it 'is accessible via upcased constant_name' do
_(Stripe::Plans::PRIMO_PLAN).wont_be_nil
end
it 'is accessible via collection' do
_(Stripe::Plans.all).must_include Stripe::Plans::PRIMO_PLAN
end
it 'is accessible via hash lookup (symbol/string agnostic)' do
_(Stripe::Plans[:primo_plan]).must_equal Stripe::Plans::PRIMO_PLAN
_(Stripe::Plans['primo_plan']).must_equal Stripe::Plans::PRIMO_PLAN
end
describe 'constant name validation' do
it 'should be invalid when providing a constant name that can not be used for Ruby constant' do
_(lambda {
Stripe.plan "Primo Plan".to_sym do |plan|
plan.name = 'Acme as a service PRIMO'
plan.constant_name = 'PRIMO PLAN'
plan.amount = 999
plan.interval = 'month'
end
}).must_raise Stripe::InvalidConfigurationError
end
end
describe 'uploading' do
include FixtureLoader
describe 'when none exists on stripe.com' do
let(:headers) { load_request_fixture('stripe_plans_headers_2017.json') }
before do
Stripe.api_version = '2018-02-04'
Stripe::Plan.stubs(:retrieve).raises(Stripe::InvalidRequestError.new("not found", "id"))
stub_request(:get, "https://api.stripe.com
gitextract_b2ydg9ox/
├── .codeclimate.yml
├── .editorconfig
├── .github/
│ ├── issue_template.md
│ ├── pull_request_template.md
│ └── workflows/
│ └── ruby.yml
├── .gitignore
├── .rubocop.yml
├── CODE_OF_CONDUCT.md
├── Changelog.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── app/
│ ├── assets/
│ │ └── stripe/
│ │ ├── stripe_elements.css
│ │ └── stripe_elements.js
│ ├── controllers/
│ │ └── stripe/
│ │ ├── application_controller.rb
│ │ └── events_controller.rb
│ ├── helpers/
│ │ └── stripe/
│ │ └── javascript_helper.rb
│ ├── models/
│ │ └── stripe/
│ │ └── event_dispatch.rb
│ └── views/
│ └── stripe/
│ ├── _elements.html.erb
│ ├── _elements_js.html.erb
│ └── _js.html.erb
├── config/
│ ├── locales/
│ │ └── en.yml
│ └── routes.rb
├── gemfiles/
│ ├── gemfiles/
│ │ └── rails70.gemfile
│ ├── rails60.gemfile
│ ├── rails61.gemfile
│ ├── rails70.gemfile
│ └── rails71.gemfile
├── lib/
│ ├── generators/
│ │ ├── stripe/
│ │ │ └── install_generator.rb
│ │ └── templates/
│ │ ├── coupons.rb
│ │ ├── plans.rb
│ │ ├── prices.rb
│ │ └── products.rb
│ ├── stripe/
│ │ ├── billing_tier.rb
│ │ ├── callbacks/
│ │ │ └── builder.rb
│ │ ├── callbacks.rb
│ │ ├── configuration_builder.rb
│ │ ├── coupons.rb
│ │ ├── current_api_version.rb
│ │ ├── engine.rb
│ │ ├── plans.rb
│ │ ├── prices.rb
│ │ ├── products.rb
│ │ ├── rails/
│ │ │ ├── tasks.rake
│ │ │ ├── testing.rb
│ │ │ └── version.rb
│ │ └── rails.rb
│ └── stripe-rails.rb
├── stripe-rails.gemspec
└── test/
├── callbacks_spec.rb
├── coupon_builder_spec.rb
├── dummy/
│ ├── README.rdoc
│ ├── Rakefile
│ ├── app/
│ │ ├── assets/
│ │ │ ├── javascripts/
│ │ │ │ └── application.js
│ │ │ └── stylesheets/
│ │ │ └── application.css
│ │ ├── controllers/
│ │ │ ├── apis_controller.rb
│ │ │ ├── application_controller.rb
│ │ │ └── stripes_controller.rb
│ │ ├── helpers/
│ │ │ └── application_helper.rb
│ │ ├── mailers/
│ │ │ └── .gitkeep
│ │ ├── models/
│ │ │ ├── .gitkeep
│ │ │ └── dummy/
│ │ │ └── model_with_callbacks.rb
│ │ └── views/
│ │ ├── layouts/
│ │ │ └── application.html.erb
│ │ └── stripes/
│ │ └── new.html.erb
│ ├── config/
│ │ ├── application.rb
│ │ ├── boot.rb
│ │ ├── database.yml
│ │ ├── environment.rb
│ │ ├── environments/
│ │ │ ├── development.rb
│ │ │ ├── production.rb
│ │ │ └── test.rb
│ │ ├── initializers/
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── inflections.rb
│ │ │ ├── mime_types.rb
│ │ │ ├── secret_token.rb
│ │ │ ├── session_store.rb
│ │ │ └── wrap_parameters.rb
│ │ ├── locales/
│ │ │ └── en.yml
│ │ ├── routes.rb
│ │ └── stripe/
│ │ ├── plans.rb
│ │ └── prices.rb
│ ├── config.ru
│ ├── lib/
│ │ ├── assets/
│ │ │ └── .gitkeep
│ │ └── dummy/
│ │ └── module_with_callbacks.rb
│ ├── log/
│ │ └── .gitkeep
│ ├── public/
│ │ ├── 404.html
│ │ ├── 422.html
│ │ └── 500.html
│ └── script/
│ └── rails
├── dummy_apis_controller_spec.rb
├── dummy_stripes_controller_spec.rb
├── event.json
├── events_controller_spec.rb
├── fixtures/
│ ├── stripe_plans.json
│ ├── stripe_plans_headers.json
│ ├── stripe_plans_headers_2017.json
│ └── stripe_prices.json
├── invoice.json
├── javascript_helper_spec.rb
├── plan_builder_spec.rb
├── price_builder_spec.rb
├── product_builder_spec.rb
├── spec_helper.rb
├── stripe_initializers_spec.rb
├── support/
│ ├── application_system_test_case.rb
│ ├── callback_helpers.rb
│ └── fixture_loader.rb
└── testing_spec.rb
SYMBOL INDEX (138 symbols across 30 files)
FILE: app/assets/stripe/stripe_elements.js
function stripeTokenHandler (line 35) | function stripeTokenHandler(token) {
FILE: app/controllers/stripe/application_controller.rb
type Stripe (line 1) | module Stripe
class ApplicationController (line 2) | class ApplicationController < ActionController::Base
FILE: app/controllers/stripe/events_controller.rb
type Stripe (line 1) | module Stripe
class EventsController (line 2) | class EventsController < ::Stripe::ApplicationController
method create (line 6) | def create
FILE: app/helpers/stripe/javascript_helper.rb
type Stripe (line 1) | module Stripe
type JavascriptHelper (line 2) | module JavascriptHelper
function stripe_javascript_tag (line 5) | def stripe_javascript_tag(stripe_js_version = DEFAULT_STRIPE_JS_VERS...
function stripe_elements_tag (line 11) | def stripe_elements_tag(submit_path:,
FILE: app/models/stripe/event_dispatch.rb
type Stripe (line 1) | module Stripe
type EventDispatch (line 2) | module EventDispatch
function dispatch_stripe_event (line 3) | def dispatch_stripe_event(request)
function retrieve_stripe_event (line 10) | def retrieve_stripe_event(request)
function webhook_event (line 27) | def webhook_event(body, sig_header, endpoint_secrets)
FILE: lib/generators/stripe/install_generator.rb
type Stripe (line 1) | module Stripe
class InstallGenerator (line 2) | class InstallGenerator < ::Rails::Generators::Base
method copy_plans_file (line 6) | def copy_plans_file
FILE: lib/stripe/billing_tier.rb
type Stripe (line 1) | module Stripe
type Plans (line 2) | module Plans
class BillingTier (line 3) | class BillingTier
method initialize (line 18) | def initialize(attrs)
method to_h (line 24) | def to_h
FILE: lib/stripe/callbacks.rb
type Stripe (line 3) | module Stripe
type Callbacks (line 4) | module Callbacks
function run_callbacks (line 136) | def run_callbacks(evt, target)
function _run_callbacks (line 141) | def _run_callbacks(type, evt, target)
function run_critical_callbacks (line 146) | def run_critical_callbacks(type, evt, target)
function run_noncritical_callbacks (line 152) | def run_noncritical_callbacks(type, evt, target)
FILE: lib/stripe/callbacks/builder.rb
type Stripe (line 1) | module Stripe
type Callbacks (line 2) | module Callbacks
type Builder (line 3) | module Builder
type ClassMethods (line 14) | module ClassMethods
function clear_callbacks! (line 20) | def clear_callbacks!
function callback (line 25) | def callback(name)
function callback_matcher (line 36) | def callback_matcher(options, block)
FILE: lib/stripe/configuration_builder.rb
type Stripe (line 3) | module Stripe
type ConfigurationBuilder (line 4) | module ConfigurationBuilder
function configuration_for (line 9) | def configuration_for(class_id, &block)
function configurations (line 25) | def configurations
function all (line 29) | def all
function [] (line 33) | def [](key)
function []= (line 37) | def []=(key, value)
function put! (line 41) | def put!
function reset! (line 45) | def reset!
class Configuration (line 51) | class Configuration
method initialize (line 55) | def initialize(id, stripe_class, stripe_configuration_class)
method finalize! (line 61) | def finalize!
method validate! (line 66) | def validate!
method globalize! (line 70) | def globalize!
method put! (line 76) | def put!
method reset! (line 85) | def reset!
method compact_create_options (line 93) | def compact_create_options
method to_s (line 97) | def to_s
method exists? (line 101) | def exists?
class InvalidConfigurationError (line 109) | class InvalidConfigurationError < StandardError
method initialize (line 112) | def initialize(errors)
FILE: lib/stripe/coupons.rb
type Stripe (line 1) | module Stripe
type Coupons (line 2) | module Coupons
function initialize (line 16) | def initialize(*args)
function repeating? (line 22) | def repeating?
function create_options (line 26) | def create_options
FILE: lib/stripe/current_api_version.rb
class CurrentApiVersion (line 1) | class CurrentApiVersion
method call (line 2) | def self.call
method after_switch_to_products_in_plans? (line 9) | def self.after_switch_to_products_in_plans?
FILE: lib/stripe/engine.rb
type Stripe (line 3) | module Stripe
class Engine (line 4) | class Engine < ::Rails::Engine
method signing_secret= (line 13) | def signing_secret=(value)
method signing_secret (line 17) | def signing_secret
method api_key= (line 22) | def stripe_config.api_key=(key)
FILE: lib/stripe/plans.rb
type Stripe (line 1) | module Stripe
type Plans (line 2) | module Plans
function initialize (line 50) | def initialize(*args)
function aggregate_usage_must_be_metered (line 58) | def aggregate_usage_must_be_metered
function name_or_product_id (line 62) | def name_or_product_id
function billing_scheme_must_be_tiered (line 66) | def billing_scheme_must_be_tiered
function tiers_must_be_array (line 70) | def tiers_must_be_array
function billing_tiers (line 74) | def billing_tiers
function validate_tiers (line 78) | def validate_tiers
type ConstTester (line 82) | module ConstTester; end
function valid_constant_name (line 83) | def valid_constant_name
function create_options (line 90) | def create_options
function default_create_options (line 98) | def default_create_options
function product_options (line 117) | def product_options
function create_options_without_products (line 123) | def create_options_without_products
FILE: lib/stripe/prices.rb
type Stripe (line 1) | module Stripe
type Prices (line 2) | module Prices
function initialize (line 57) | def initialize(*args)
function put! (line 67) | def put!
function reset! (line 77) | def reset!
function exists? (line 82) | def exists?
function stripe_object (line 88) | def stripe_object
function stripe_id (line 94) | def stripe_id
function recurring_interval (line 98) | def recurring_interval
function recurring_aggregate_usage (line 102) | def recurring_aggregate_usage
function recurring_usage_type (line 106) | def recurring_usage_type
function recurring_interval_count (line 110) | def recurring_interval_count
function recurring_aggregate_usage_must_be_metered (line 115) | def recurring_aggregate_usage_must_be_metered
function recurring_interval_count_maximum (line 119) | def recurring_interval_count_maximum
function name_or_product_id (line 128) | def name_or_product_id
function billing_scheme_must_be_tiered (line 132) | def billing_scheme_must_be_tiered
function tiers_must_be_array (line 136) | def tiers_must_be_array
function billing_tiers (line 140) | def billing_tiers
function validate_tiers (line 144) | def validate_tiers
type ConstTester (line 148) | module ConstTester; end
function valid_constant_name (line 149) | def valid_constant_name
function reset_options (line 156) | def reset_options
function create_options (line 164) | def create_options
function product_options (line 181) | def product_options
FILE: lib/stripe/products.rb
type Stripe (line 1) | module Stripe
type Products (line 2) | module Products
function good? (line 28) | def good?
function service? (line 32) | def service?
function create_options (line 36) | def create_options
FILE: lib/stripe/rails/testing.rb
type Stripe (line 9) | module Stripe
type Rails (line 10) | module Rails
type Testing (line 11) | module Testing
function send_event (line 12) | def self.send_event(event, properties = {})
FILE: lib/stripe/rails/version.rb
type Stripe (line 1) | module Stripe
type Rails (line 2) | module Rails
FILE: test/dummy/app/controllers/apis_controller.rb
class ApisController (line 3) | class ApisController < ApiControllerKlass
method index (line 4) | def index
FILE: test/dummy/app/controllers/application_controller.rb
class ApplicationController (line 1) | class ApplicationController < ActionController::Base
FILE: test/dummy/app/controllers/stripes_controller.rb
class StripesController (line 1) | class StripesController < ApplicationController
method new (line 2) | def new
FILE: test/dummy/app/helpers/application_helper.rb
type ApplicationHelper (line 1) | module ApplicationHelper
FILE: test/dummy/app/models/dummy/model_with_callbacks.rb
class Dummy::ModelWithCallbacks (line 1) | class Dummy::ModelWithCallbacks
FILE: test/dummy/config/application.rb
type Dummy (line 11) | module Dummy
class Application (line 12) | class Application < Rails::Application
FILE: test/dummy/lib/dummy/module_with_callbacks.rb
class Dummy::ModuleWithCallbacks (line 1) | class Dummy::ModuleWithCallbacks
FILE: test/dummy_stripes_controller_spec.rb
class DummyStripesControllerSpec (line 3) | class DummyStripesControllerSpec < ApplicationSystemTestCase
FILE: test/stripe_initializers_spec.rb
function rerun_initializers! (line 16) | def rerun_initializers!
FILE: test/support/application_system_test_case.rb
class ApplicationSystemTestCase (line 1) | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
FILE: test/support/callback_helpers.rb
type CallbackHelpers (line 1) | module CallbackHelpers
function type= (line 2) | def type=(type)
function run_callback_with (line 8) | def run_callback_with(callback)
FILE: test/support/fixture_loader.rb
type FixtureLoader (line 1) | module FixtureLoader
function load_request_fixture (line 2) | def load_request_fixture(name)
Condensed preview — 109 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (231K chars).
[
{
"path": ".codeclimate.yml",
"chars": 377,
"preview": "---\nengines:\n brakeman:\n enabled: true\n duplication:\n enabled: true\n config:\n languages:\n - ruby\n "
},
{
"path": ".editorconfig",
"chars": 130,
"preview": "# editorconfig.org\nroot = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\n\n[*.rb]\nindent_style = space\nindent_size = 2\ncharse"
},
{
"path": ".github/issue_template.md",
"chars": 55,
"preview": "<!--\n Please give us ~4 weeks to get back to you.\n-->\n"
},
{
"path": ".github/pull_request_template.md",
"chars": 350,
"preview": "<!--\n Thanks a bunch for helping out with the project!\n\n Please remember to,\n\n 1. Add tests if they do not exist, fix"
},
{
"path": ".github/workflows/ruby.yml",
"chars": 1265,
"preview": "name: Ruby\n\non:\n push:\n branches:\n - master\n pull_request:\n branches:\n - master\n\njobs:\n build:\n ru"
},
{
"path": ".gitignore",
"chars": 221,
"preview": "*.gem\ngemfiles/*.lock\n*.rbc\n.bundle\n.config\n.yardoc\nGemfile.lock\nInstalledFiles\n_yardoc\ncoverage\ndoc/\nlib/bundler/man\npk"
},
{
"path": ".rubocop.yml",
"chars": 37700,
"preview": "AllCops:\n DisabledByDefault: true\n\n#################### Lint ################################\n\nLint/AmbiguousOperator:\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 2398,
"preview": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, and in the interest of\nfostering an open"
},
{
"path": "Changelog.md",
"chars": 8923,
"preview": "## Next Release\n\n## 2.6.0 (2024-08-12)\n- Add the following callbacks (Thanks @4nd2in !):\n * `checkout.session.async_pay"
},
{
"path": "Gemfile",
"chars": 354,
"preview": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in stripe-rails.gemspec\ngemspec\n\ngem 'rake'\n\ngroup :dev"
},
{
"path": "LICENSE",
"chars": 1090,
"preview": "Copyright (c) 2012-2017 The Frontside Software, Inc.\n\nMIT License\n\nPermission is hereby granted, free of charge, to any "
},
{
"path": "README.md",
"chars": 21618,
"preview": "# Stripe::Rails: A Rails Engine for use with [stripe.com](https://stripe.com)\n[ do |t|\n t.libs << '"
},
{
"path": "app/assets/stripe/stripe_elements.css",
"chars": 449,
"preview": ".StripeElement {\n background-color: white;\n height: 40px;\n padding: 10px 12px;\n border-radius: 4px;\n border: 1px so"
},
{
"path": "app/assets/stripe/stripe_elements.js",
"chars": 1370,
"preview": "// Create a Stripe client.\nvar stripe = Stripe(window.stripe_publishable_key);\n\n// Create an instance of Elements.\nvar e"
},
{
"path": "app/controllers/stripe/application_controller.rb",
"chars": 189,
"preview": "module Stripe\n class ApplicationController < ActionController::Base\n skip_before_action(:verify_authenticity_token) "
},
{
"path": "app/controllers/stripe/events_controller.rb",
"chars": 456,
"preview": "module Stripe\n class EventsController < ::Stripe::ApplicationController\n include Stripe::EventDispatch\n respond_t"
},
{
"path": "app/helpers/stripe/javascript_helper.rb",
"chars": 829,
"preview": "module Stripe\n module JavascriptHelper\n DEFAULT_STRIPE_JS_VERSION = 'v3'\n\n def stripe_javascript_tag(stripe_js_ve"
},
{
"path": "app/models/stripe/event_dispatch.rb",
"chars": 1073,
"preview": "module Stripe\n module EventDispatch\n def dispatch_stripe_event(request)\n retrieve_stripe_event(request) do |evt"
},
{
"path": "app/views/stripe/_elements.html.erb",
"chars": 659,
"preview": "<%= stylesheet_link_tag css_path, media: 'all' %>\n\n<div>\n <div id=\"card-errors\">\n </div>\n\n <%= form_tag submit_path, "
},
{
"path": "app/views/stripe/_elements_js.html.erb",
"chars": 0,
"preview": ""
},
{
"path": "app/views/stripe/_js.html.erb",
"chars": 1333,
"preview": "<%- stripe_js_version = Stripe::JavascriptHelper::DEFAULT_STRIPE_JS_VERSION if (!defined?(stripe_js_version) || stripe_j"
},
{
"path": "config/locales/en.yml",
"chars": 115,
"preview": "en:\n stripe_rails:\n elements:\n label_text: Credit or debit card\n submit_button_text: Submit payment\n\n"
},
{
"path": "config/routes.rb",
"chars": 230,
"preview": "Rails.application.routes.draw do\n if Rails.application.config.stripe.auto_mount\n mount Stripe::Engine => Rails.appli"
},
{
"path": "gemfiles/gemfiles/rails70.gemfile",
"chars": 405,
"preview": "source :rubygems\n\ngem 'rails', '~> 7.0.0'\n\ngem 'rake'\ngem 'responders'\ngem 'stripe'\n\ngroup :development, :test do\n gem "
},
{
"path": "gemfiles/rails60.gemfile",
"chars": 343,
"preview": "source :rubygems\n\ngem 'rails', '~> 6.0.0'\n\ngem 'rake'\ngem 'responders'\ngem 'stripe'\n\ngroup :development, :test do\n gem "
},
{
"path": "gemfiles/rails61.gemfile",
"chars": 343,
"preview": "source :rubygems\n\ngem 'rails', '~> 6.1.0'\n\ngem 'rake'\ngem 'responders'\ngem 'stripe'\n\ngroup :development, :test do\n gem "
},
{
"path": "gemfiles/rails70.gemfile",
"chars": 343,
"preview": "source :rubygems\n\ngem 'rails', '~> 7.0.0'\n\ngem 'rake'\ngem 'responders'\ngem 'stripe'\n\ngroup :development, :test do\n gem "
},
{
"path": "gemfiles/rails71.gemfile",
"chars": 343,
"preview": "source :rubygems\n\ngem 'rails', '~> 7.1.0'\n\ngem 'rake'\ngem 'responders'\ngem 'stripe'\n\ngroup :development, :test do\n gem "
},
{
"path": "lib/generators/stripe/install_generator.rb",
"chars": 422,
"preview": "module Stripe\n class InstallGenerator < ::Rails::Generators::Base\n source_root ::File.expand_path(\"../../templates\","
},
{
"path": "lib/generators/templates/coupons.rb",
"chars": 1223,
"preview": "# This file contains descriptions of all your statically defined\n# stripe coupons. You may wish to define unique one-off"
},
{
"path": "lib/generators/templates/plans.rb",
"chars": 836,
"preview": "# This file contains descriptions of all your stripe plans\n\n# Example\n# Stripe::Plans::PRIMO #=> 'primo'\n\n# Stripe.plan "
},
{
"path": "lib/generators/templates/prices.rb",
"chars": 1529,
"preview": "# This file contains descriptions of all your stripe prices\n\n# Example\n# Stripe::Prices::LITE.lookup_key #=> 'lite'\n\n# P"
},
{
"path": "lib/generators/templates/products.rb",
"chars": 522,
"preview": "# This file contains descriptions of all your stripe products\n\n# Example\n# Stripe::Products::PRIMO #=> 'primo'\n\n# Stripe"
},
{
"path": "lib/stripe/billing_tier.rb",
"chars": 1158,
"preview": "module Stripe\n module Plans\n class BillingTier\n include ActiveModel::Validations\n\n validates_presence_of :"
},
{
"path": "lib/stripe/callbacks/builder.rb",
"chars": 1969,
"preview": "module Stripe\n module Callbacks\n module Builder\n extend ActiveSupport::Concern\n\n included do\n exten"
},
{
"path": "lib/stripe/callbacks.rb",
"chars": 5697,
"preview": "require 'stripe/callbacks/builder'\n\nmodule Stripe\n module Callbacks\n include Callbacks::Builder\n\n callback 'accou"
},
{
"path": "lib/stripe/configuration_builder.rb",
"chars": 3022,
"preview": "require \"active_model\"\n\nmodule Stripe\n module ConfigurationBuilder\n extend ActiveSupport::Concern\n\n included do\n "
},
{
"path": "lib/stripe/coupons.rb",
"chars": 1449,
"preview": "module Stripe\n module Coupons\n include ConfigurationBuilder\n\n configuration_for :coupon do\n attr_accessor :n"
},
{
"path": "lib/stripe/current_api_version.rb",
"chars": 304,
"preview": "class CurrentApiVersion\n def self.call\n Stripe.api_version || begin\n resp, _ = Stripe::Plan.request(:get, Strip"
},
{
"path": "lib/stripe/engine.rb",
"chars": 3725,
"preview": "require 'stripe'\n\nmodule Stripe\n class Engine < ::Rails::Engine\n isolate_namespace Stripe\n\n class << self\n a"
},
{
"path": "lib/stripe/plans.rb",
"chars": 4858,
"preview": "module Stripe\n module Plans\n include ConfigurationBuilder\n\n configuration_for :plan do\n attr_accessor :activ"
},
{
"path": "lib/stripe/prices.rb",
"chars": 7044,
"preview": "module Stripe\n module Prices\n include ConfigurationBuilder\n VALID_TIME_UNITS = %i(day week month year)\n\n confi"
},
{
"path": "lib/stripe/products.rb",
"chars": 1388,
"preview": "module Stripe\n module Products\n include ConfigurationBuilder\n\n configuration_for :product do\n attr_accessor "
},
{
"path": "lib/stripe/rails/tasks.rake",
"chars": 1278,
"preview": "namespace :stripe do\n desc 'verify your stripe.com authentication configuration'\n task 'verify' => :environment do\n "
},
{
"path": "lib/stripe/rails/testing.rb",
"chars": 471,
"preview": "begin\n require 'stripe_mock'\nrescue LoadError\n warn %q{Please add \"gem 'stripe-ruby-mock', group: 'test'\"\" to the Gemf"
},
{
"path": "lib/stripe/rails/version.rb",
"chars": 68,
"preview": "module Stripe\n module Rails\n VERSION = '2.6.0'.freeze\n end\nend\n"
},
{
"path": "lib/stripe/rails.rb",
"chars": 307,
"preview": "require 'responders'\nrequire \"stripe/rails/version\"\nrequire 'stripe/engine'\nrequire 'stripe/configuration_builder'\nrequi"
},
{
"path": "lib/stripe-rails.rb",
"chars": 24,
"preview": "require 'stripe/rails'\n\n"
},
{
"path": "stripe-rails.gemspec",
"chars": 870,
"preview": "require File.expand_path('lib/stripe/rails/version', __dir__)\n\nGem::Specification.new do |gem|\n gem.authors = [\"C"
},
{
"path": "test/callbacks_spec.rb",
"chars": 5201,
"preview": "require 'spec_helper'\n\ndescribe Stripe::Callbacks do\n include Rack::Test::Methods\n include CallbackHelpers\n\n let(:app"
},
{
"path": "test/coupon_builder_spec.rb",
"chars": 3667,
"preview": "require 'spec_helper'\n\ndescribe 'building coupons' do\n before do\n Stripe::Coupons.configurations.clear\n end\n descr"
},
{
"path": "test/dummy/README.rdoc",
"chars": 9208,
"preview": "== Welcome to Rails\n\nRails is a web-application framework that includes everything needed to create\ndatabase-backed web "
},
{
"path": "test/dummy/Rakefile",
"chars": 270,
"preview": "#!/usr/bin/env rake\n# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistran"
},
{
"path": "test/dummy/app/assets/javascripts/application.js",
"chars": 641,
"preview": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//"
},
{
"path": "test/dummy/app/assets/stylesheets/application.css",
"chars": 546,
"preview": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below"
},
{
"path": "test/dummy/app/controllers/apis_controller.rb",
"chars": 186,
"preview": "ApiControllerKlass = defined?(ActionController::API) ? ActionController::API : ApplicationController\n\nclass ApisControll"
},
{
"path": "test/dummy/app/controllers/application_controller.rb",
"chars": 80,
"preview": "class ApplicationController < ActionController::Base\n protect_from_forgery\nend\n"
},
{
"path": "test/dummy/app/controllers/stripes_controller.rb",
"chars": 67,
"preview": "class StripesController < ApplicationController\n def new\n end\nend"
},
{
"path": "test/dummy/app/helpers/application_helper.rb",
"chars": 29,
"preview": "module ApplicationHelper\nend\n"
},
{
"path": "test/dummy/app/mailers/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "test/dummy/app/models/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "test/dummy/app/models/dummy/model_with_callbacks.rb",
"chars": 35,
"preview": "class Dummy::ModelWithCallbacks\nend"
},
{
"path": "test/dummy/app/views/layouts/application.html.erb",
"chars": 123,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>Dummy</title>\n <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</htm"
},
{
"path": "test/dummy/app/views/stripes/new.html.erb",
"chars": 212,
"preview": "<%= stripe_javascript_tag (params[:version] || :v3) %>\n\n<script>\n if (typeof(Stripe) != 'undefined') {\n document.wri"
},
{
"path": "test/dummy/config/application.rb",
"chars": 2638,
"preview": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails'\nrequire 'action_controller/railtie'\nrequire 'rails/test_u"
},
{
"path": "test/dummy/config/boot.rb",
"chars": 235,
"preview": "require 'rubygems'\ngemfile = File.expand_path('../../../../Gemfile', __FILE__)\n\nif File.exist?(gemfile)\n ENV['BUNDLE_GE"
},
{
"path": "test/dummy/config/database.yml",
"chars": 576,
"preview": "# SQLite version 3.x\n# gem install sqlite3\n#\n# Ensure the SQLite 3 gem is defined in your Gemfile\n# gem 'sqlite3'\n"
},
{
"path": "test/dummy/config/environment.rb",
"chars": 149,
"preview": "# Load the rails application\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the rails application\nDu"
},
{
"path": "test/dummy/config/environments/development.rb",
"chars": 1410,
"preview": "Dummy::Application.configure do\n config.eager_load = false\n # Settings specified here will take precedence over those "
},
{
"path": "test/dummy/config/environments/production.rb",
"chars": 2479,
"preview": "Dummy::Application.configure do\n # Settings specified here will take precedence over those in config/application.rb\n\n "
},
{
"path": "test/dummy/config/environments/test.rb",
"chars": 1453,
"preview": "Dummy::Application.configure do\n # Settings specified here will take precedence over those in config/application.rb\n\n "
},
{
"path": "test/dummy/config/initializers/backtrace_silencers.rb",
"chars": 404,
"preview": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're"
},
{
"path": "test/dummy/config/initializers/inflections.rb",
"chars": 533,
"preview": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format\n# (al"
},
{
"path": "test/dummy/config/initializers/mime_types.rb",
"chars": 205,
"preview": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::"
},
{
"path": "test/dummy/config/initializers/secret_token.rb",
"chars": 575,
"preview": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed coo"
},
{
"path": "test/dummy/config/initializers/session_store.rb",
"chars": 407,
"preview": "# Be sure to restart your server when you modify this file.\n\nDummy::Application.config.session_store :cookie_store, key:"
},
{
"path": "test/dummy/config/initializers/wrap_parameters.rb",
"chars": 465,
"preview": "# Be sure to restart your server when you modify this file.\n#\n# This file contains settings for ActionController::Params"
},
{
"path": "test/dummy/config/locales/en.yml",
"chars": 214,
"preview": "# Sample localization file for English. Add more files in this directory for other locales.\n# See https://github.com/sve"
},
{
"path": "test/dummy/config/routes.rb",
"chars": 1853,
"preview": "Dummy::Application.routes.draw do\n resources :stripes, only: :new\n resources :apis, only: :index\n # The priority is b"
},
{
"path": "test/dummy/config/stripe/plans.rb",
"chars": 1058,
"preview": "Stripe.plan :gold do |plan|\n plan.name = 'Solid Gold'\n plan.amount = 699\n plan.interval = 'month'\nend\n\nStripe.plan"
},
{
"path": "test/dummy/config/stripe/prices.rb",
"chars": 1338,
"preview": "Stripe.price :gold do |price|\n price.name = 'Solid Gold'\n price.unit_amount = 699\n price.recurring = {\n interv"
},
{
"path": "test/dummy/config.ru",
"chars": 155,
"preview": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',"
},
{
"path": "test/dummy/lib/assets/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "test/dummy/lib/dummy/module_with_callbacks.rb",
"chars": 36,
"preview": "class Dummy::ModuleWithCallbacks\nend"
},
{
"path": "test/dummy/log/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "test/dummy/public/404.html",
"chars": 728,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>The page you were looking for doesn't exist (404)</title>\n <style type=\"text/css"
},
{
"path": "test/dummy/public/422.html",
"chars": 711,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>The change you wanted was rejected (422)</title>\n <style type=\"text/css\">\n bo"
},
{
"path": "test/dummy/public/500.html",
"chars": 643,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <title>We're sorry, but something went wrong (500)</title>\n <style type=\"text/css\">\n "
},
{
"path": "test/dummy/script/rails",
"chars": 295,
"preview": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails 3 gems installed from the r"
},
{
"path": "test/dummy_apis_controller_spec.rb",
"chars": 329,
"preview": "require 'spec_helper'\n\ndescribe ApisController do\n include Rack::Test::Methods\n\n let(:app) { Rails.application }\n bef"
},
{
"path": "test/dummy_stripes_controller_spec.rb",
"chars": 551,
"preview": "require 'spec_helper'\n\nclass DummyStripesControllerSpec < ApplicationSystemTestCase\n setup do\n Dummy::Application.co"
},
{
"path": "test/event.json",
"chars": 383,
"preview": "{\n \"api_version\": \"2017-02-14\",\n \"created\": 1234567890,\n \"customer_email\": \"\",\n \"data\": {\n \"object\": {\n }\n },"
},
{
"path": "test/events_controller_spec.rb",
"chars": 2940,
"preview": "require 'spec_helper'\n\ndescribe Stripe::EventsController do\n include Rack::Test::Methods\n\n let(:app) { Rails.applicati"
},
{
"path": "test/fixtures/stripe_plans.json",
"chars": 94,
"preview": "{\n \"object\": \"list\",\n \"count\": 0,\n \"data\": [],\n \"has_more\": false,\n \"url\": \"/v1/plans\"\n}\n"
},
{
"path": "test/fixtures/stripe_plans_headers.json",
"chars": 682,
"preview": "{\n \"Server\": \"nginx\",\n \"Date\": \"Wed, 21 Mar 2018 20:04:56 GMT\",\n \"Content-Type\": \"application/json\",\n \"Content-Lengt"
},
{
"path": "test/fixtures/stripe_plans_headers_2017.json",
"chars": 682,
"preview": "{\n \"Server\": \"nginx\",\n \"Date\": \"Wed, 21 Mar 2018 20:04:56 GMT\",\n \"Content-Type\": \"application/json\",\n \"Content-Lengt"
},
{
"path": "test/fixtures/stripe_prices.json",
"chars": 95,
"preview": "{\n \"object\": \"list\",\n \"count\": 0,\n \"data\": [],\n \"has_more\": false,\n \"url\": \"/v1/prices\"\n}\n"
},
{
"path": "test/invoice.json",
"chars": 1900,
"preview": "{\n \"amount_due\": 0,\n \"application_fee\": 0,\n \"attempt_count\": 0,\n \"attempted\": true,\n \"billing\": \"\",\n \"charge\": \"\","
},
{
"path": "test/javascript_helper_spec.rb",
"chars": 2857,
"preview": "require 'spec_helper'\n\ndescribe Stripe::JavascriptHelper do\n before { Rails.application.config.stripe.publishable_key ="
},
{
"path": "test/plan_builder_spec.rb",
"chars": 16114,
"preview": "require 'spec_helper'\n\ndescribe 'building plans' do\n describe 'simply' do\n before do\n Stripe.plan :primo do |pl"
},
{
"path": "test/price_builder_spec.rb",
"chars": 19398,
"preview": "require 'spec_helper'\n\ndescribe 'building prices' do\n describe 'simply' do\n before do\n Stripe.price :lite do |p"
},
{
"path": "test/product_builder_spec.rb",
"chars": 3235,
"preview": "require 'spec_helper'\n\ndescribe 'building products' do\n before do\n Stripe.product :primo do |product|\n product."
},
{
"path": "test/spec_helper.rb",
"chars": 1016,
"preview": "require \"simplecov\"\nSimpleCov.start do\n add_filter \"/test/\"\nend\n\nrequire 'minitest/autorun'\n\nrequire 'webmock/minitest'"
},
{
"path": "test/stripe_initializers_spec.rb",
"chars": 4493,
"preview": "require 'spec_helper'\n\ndescribe \"Configuring the stripe engine\" do\n i_suck_and_my_tests_are_order_dependent! # the defa"
},
{
"path": "test/support/application_system_test_case.rb",
"chars": 107,
"preview": "class ApplicationSystemTestCase < ActionDispatch::SystemTestCase\n driven_by :selenium_chrome_headless\nend\n"
},
{
"path": "test/support/callback_helpers.rb",
"chars": 345,
"preview": "module CallbackHelpers\n def type=(type)\n content['type'] = type\n @stubbed_event = Stripe::Event.construct_from(co"
},
{
"path": "test/support/fixture_loader.rb",
"chars": 131,
"preview": "module FixtureLoader\n def load_request_fixture(name)\n Pathname.new(__FILE__).join('..', '..', 'fixtures', name).read"
},
{
"path": "test/testing_spec.rb",
"chars": 1681,
"preview": "require 'spec_helper'\nrequire 'stripe/rails/testing'\n\ndescribe \"Testing\" do\n let(:observer) { Class.new }\n let(:event"
}
]
About this extraction
This page contains the full source code of the thefrontside/stripe-rails GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 109 files (212.1 KB), approximately 56.5k tokens, and a symbol index with 138 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.