{"record":{"id":"1ea8b219d6703129","repo":"antiwork/gumroad","slug":"all-tiers-must-have-the-same-set-of-payment-option","errorCode":null,"errorMessage":"All tiers must have the same set of payment options.","messagePattern":"All tiers must have the same set of payment options\\.","errorType":"validation","errorClass":"Link::LinkInvalid","httpStatus":null,"severity":"error","filePath":"app/services/product/variant_category_updater_service.rb","lineNumber":618,"sourceCode":"        )\n          errors.add(:base, \"Please provide a price for the default payment option.\")\n          raise Link::LinkInvalid, \"Please provide a price for the default payment option.\"\n        end\n      end\n\n      # error if variants have different recurrence options:\n      # 1. Extract variant recurrence selections:\n      # Ex. [[\"monthly\", \"yearly\"], [\"monthly\"]]\n      enabled_recurrences_for_variants = variants.map do |variant|\n        variant[:recurrence_price_values].select { |k, v| v[:enabled] }.keys.sort\n      end\n      # 2. Ensure that they match\n      # Ex. [\"monthly\", \"yearly\"] != [\"monthly\"] raises error\n      enabled_recurrences_for_variants.each_with_index do |recurrences, index|\n        next_recurrences = enabled_recurrences_for_variants[index + 1]\n        if next_recurrences && recurrences != next_recurrences\n          errors.add(:base, \"All tiers must have the same set of payment options.\")\n          raise Link::LinkInvalid, \"All tiers must have the same set of payment options.\"\n        end\n      end\n    end\nend\n","sourceCodeStart":600,"sourceCodeEnd":623,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/services/product/variant_category_updater_service.rb#L600-L623","documentation":"The final pass of validate_variant_recurrences!: each variant's enabled recurrence keys (from recurrence_price_values, selected and sorted) are compared pairwise across tiers; the first adjacent mismatch (e.g. ['monthly','yearly'] vs ['monthly']) adds the error and raises Link::LinkInvalid. The product enforces that every tier exposes exactly the same set of payment options — mixed option sets across tiers are not representable.","triggerScenarios":"Tier 1 enables monthly+yearly while Tier 2 enables only yearly; any tier disabling an option that the others keep enabled.","commonSituations":"Adding a new tier later and forgetting to check both periods; toggling one period off on a single tier in the editor grid.","solutions":["Open each tier's pricing grid and check exactly the same recurrence options on all tiers","Or disable the extra option on every tier so the sets match","Re-save after aligning; the pairwise comparison reports the first mismatched pair, but auditing all tiers at once is faster"],"exampleFix":"# before\ntier1_recurrences = { 'monthly' => {...}, 'yearly' => {...} }\ntier2_recurrences = { 'yearly' => {...} } # mismatch\n# => Link::LinkInvalid: All tiers must have the same set of payment options.\n\n# after\ntier2_recurrences = { 'monthly' => {...}, 'yearly' => {...} } # same enabled set","handlingStrategy":"validation","validationCode":"enabled_sets = variants.map { |v| v[:recurrence_price_values].select { |_k, val| val[:enabled] }.keys.sort }\nraise 'tiers have different payment options' unless enabled_sets.uniq.one?","typeGuard":"def all_tiers_same_recurrence_options?(variants)\n  variants.map { |v| v[:recurrence_price_values].select { |_k, val| val[:enabled] }.keys.sort }.uniq.one?\nend","tryCatchPattern":"begin\n  VariantCategoryUpdaterService.new(product:, category_params:).process\nrescue Link::LinkInvalid\n  render_edit_form_with(product.errors.full_messages)\nend","preventionTips":["Make the recurrence checkboxes shared across tiers in the editor so they cannot diverge","After adding a new tier, copy the payment-option set from an existing tier before pricing it","The server compares the sorted enabled-key SETS — a tier enabling an extra option fails even if all its prices are valid"],"tags":["ruby","rails","membership","pricing","product-validation"],"backgroundTag":"tier-configuration-mismatch","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}