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