paddle_rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +294 -0
- data/Rakefile +6 -0
- data/app/assets/stylesheets/paddle_rails/application.css +16 -0
- data/app/assets/stylesheets/paddle_rails/tailwind.css +1824 -0
- data/app/assets/tailwind/application.css +1 -0
- data/app/controllers/concerns/paddle_rails/paddle_checkout_error_handler.rb +89 -0
- data/app/controllers/concerns/paddle_rails/subscription_owner.rb +16 -0
- data/app/controllers/paddle_rails/application_controller.rb +21 -0
- data/app/controllers/paddle_rails/checkout_controller.rb +121 -0
- data/app/controllers/paddle_rails/dashboard_controller.rb +37 -0
- data/app/controllers/paddle_rails/onboarding_controller.rb +55 -0
- data/app/controllers/paddle_rails/payments_controller.rb +62 -0
- data/app/controllers/paddle_rails/subscriptions_controller.rb +92 -0
- data/app/controllers/paddle_rails/webhooks_controller.rb +78 -0
- data/app/helpers/paddle_rails/application_helper.rb +121 -0
- data/app/helpers/paddle_rails/subscription_owner_helper.rb +14 -0
- data/app/jobs/paddle_rails/application_job.rb +4 -0
- data/app/jobs/paddle_rails/process_webhook_job.rb +38 -0
- data/app/mailers/paddle_rails/application_mailer.rb +6 -0
- data/app/models/concerns/paddle_rails/subscribable.rb +46 -0
- data/app/models/paddle_rails/application_record.rb +5 -0
- data/app/models/paddle_rails/payment.rb +43 -0
- data/app/models/paddle_rails/price.rb +25 -0
- data/app/models/paddle_rails/product.rb +16 -0
- data/app/models/paddle_rails/subscription.rb +87 -0
- data/app/models/paddle_rails/subscription_item.rb +35 -0
- data/app/models/paddle_rails/webhook_event.rb +51 -0
- data/app/presenters/paddle_rails/payment_presenter.rb +96 -0
- data/app/presenters/paddle_rails/product_presenter.rb +178 -0
- data/app/presenters/paddle_rails/subscription_presenter.rb +145 -0
- data/app/views/layouts/paddle_rails/application.html.erb +170 -0
- data/app/views/paddle_rails/checkout/show.html.erb +128 -0
- data/app/views/paddle_rails/dashboard/_change_plan.html.erb +286 -0
- data/app/views/paddle_rails/dashboard/_current_subscription.html.erb +66 -0
- data/app/views/paddle_rails/dashboard/_payment_history.html.erb +79 -0
- data/app/views/paddle_rails/dashboard/_payment_method.html.erb +48 -0
- data/app/views/paddle_rails/dashboard/show.html.erb +47 -0
- data/app/views/paddle_rails/onboarding/show.html.erb +100 -0
- data/app/views/paddle_rails/shared/configuration_error.html.erb +94 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20251124180624_create_paddle_rails_subscription_plans.rb +18 -0
- data/db/migrate/20251124180817_create_paddle_rails_subscription_prices.rb +26 -0
- data/db/migrate/20251127221947_create_paddle_rails_webhook_events.rb +19 -0
- data/db/migrate/20251128135831_create_paddle_rails_subscriptions.rb +21 -0
- data/db/migrate/20251128142327_create_paddle_rails_subscription_items.rb +16 -0
- data/db/migrate/20251128151334_remove_paddle_price_id_from_subscriptions.rb +7 -0
- data/db/migrate/20251128151401_rename_subscription_plans_to_products.rb +6 -0
- data/db/migrate/20251128151402_rename_subscription_plan_id_to_subscription_product_id.rb +13 -0
- data/db/migrate/20251128151453_remove_subscription_price_id_from_subscriptions.rb +8 -0
- data/db/migrate/20251128151501_add_subscription_product_id_to_subscription_items.rb +8 -0
- data/db/migrate/20251128152025_remove_paddle_item_id_from_subscription_items.rb +6 -0
- data/db/migrate/20251128212046_rename_subscription_products_to_products.rb +6 -0
- data/db/migrate/20251128212047_rename_subscription_prices_to_prices.rb +6 -0
- data/db/migrate/20251128212053_rename_subscription_product_id_to_product_id_in_prices.rb +13 -0
- data/db/migrate/20251128212054_rename_fks_in_subscription_items.rb +20 -0
- data/db/migrate/20251128220016_add_scheduled_cancelation_at_to_subscriptions.rb +6 -0
- data/db/migrate/20251129121336_add_payment_method_to_subscriptions.rb +10 -0
- data/db/migrate/20251129222345_create_paddle_rails_payments.rb +24 -0
- data/lib/paddle_rails/checkout.rb +181 -0
- data/lib/paddle_rails/configuration.rb +121 -0
- data/lib/paddle_rails/engine.rb +49 -0
- data/lib/paddle_rails/product_sync.rb +176 -0
- data/lib/paddle_rails/subscription_sync.rb +303 -0
- data/lib/paddle_rails/version.rb +6 -0
- data/lib/paddle_rails/webhook_processor.rb +102 -0
- data/lib/paddle_rails/webhook_verifier.rb +110 -0
- data/lib/paddle_rails.rb +32 -0
- data/lib/tasks/paddle_rails_tasks.rake +15 -0
- metadata +157 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaddleRails
|
|
4
|
+
# Service class for processing webhook events.
|
|
5
|
+
#
|
|
6
|
+
# Delegates to specific handlers based on event type and emits
|
|
7
|
+
# ActiveSupport::Notifications for host applications to listen to.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# PaddleRails::WebhookProcessor.process(webhook_event)
|
|
11
|
+
class WebhookProcessor
|
|
12
|
+
# Process a webhook event.
|
|
13
|
+
#
|
|
14
|
+
# @param event [PaddleRails::WebhookEvent] The webhook event to process
|
|
15
|
+
# @return [void]
|
|
16
|
+
def self.process(event)
|
|
17
|
+
new(event).process
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param event [PaddleRails::WebhookEvent]
|
|
21
|
+
def initialize(event)
|
|
22
|
+
@event = event
|
|
23
|
+
@payload = event.payload
|
|
24
|
+
@event_type = event.event_type
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Process the webhook event.
|
|
28
|
+
#
|
|
29
|
+
# Emits an ActiveSupport::Notification with the event type and payload
|
|
30
|
+
# so host applications can subscribe to specific events.
|
|
31
|
+
#
|
|
32
|
+
# @return [void]
|
|
33
|
+
def process
|
|
34
|
+
# Emit notification for host applications to listen to
|
|
35
|
+
# Format: "paddle_rails.{event_type}"
|
|
36
|
+
notification_name = "paddle_rails.#{@event_type}"
|
|
37
|
+
|
|
38
|
+
ActiveSupport::Notifications.instrument(notification_name) do |payload|
|
|
39
|
+
payload[:webhook_event] = @event
|
|
40
|
+
payload[:event_type] = @event_type
|
|
41
|
+
payload[:raw_payload] = @payload
|
|
42
|
+
|
|
43
|
+
# Delegate to specific handler if it exists
|
|
44
|
+
handler_method = handler_method_name
|
|
45
|
+
if respond_to?(handler_method, true)
|
|
46
|
+
send(handler_method)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# Get the handler method name for the event type.
|
|
54
|
+
#
|
|
55
|
+
# Converts "subscription.created" to "handle_subscription_created"
|
|
56
|
+
#
|
|
57
|
+
# @return [String] The handler method name
|
|
58
|
+
def handler_method_name
|
|
59
|
+
"handle_#{@event_type.tr('.', '_')}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Handler for subscription.created events.
|
|
63
|
+
def handle_subscription_created
|
|
64
|
+
subscription_data = @payload["data"]
|
|
65
|
+
return unless subscription_data
|
|
66
|
+
|
|
67
|
+
SubscriptionSync.sync_from_payload(subscription_data)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Handler for subscription.updated events.
|
|
71
|
+
def handle_subscription_updated
|
|
72
|
+
subscription_data = @payload["data"]
|
|
73
|
+
return unless subscription_data
|
|
74
|
+
|
|
75
|
+
SubscriptionSync.sync_from_payload(subscription_data)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Handler for subscription.canceled events.
|
|
79
|
+
def handle_subscription_canceled
|
|
80
|
+
subscription_data = @payload["data"]
|
|
81
|
+
return unless subscription_data
|
|
82
|
+
|
|
83
|
+
SubscriptionSync.sync_from_payload(subscription_data)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Handler for transaction.completed events.
|
|
87
|
+
#
|
|
88
|
+
# Syncs payment record and payment method details from the completed transaction
|
|
89
|
+
# to the associated subscription.
|
|
90
|
+
def handle_transaction_completed
|
|
91
|
+
transaction_data = @payload["data"]
|
|
92
|
+
return unless transaction_data
|
|
93
|
+
|
|
94
|
+
# Sync payment record
|
|
95
|
+
SubscriptionSync.sync_payment(transaction_data)
|
|
96
|
+
|
|
97
|
+
# Sync payment method from the transaction
|
|
98
|
+
SubscriptionSync.sync_payment_method_from_transaction(transaction_data)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module PaddleRails
|
|
6
|
+
# Service class for verifying Paddle webhook signatures.
|
|
7
|
+
#
|
|
8
|
+
# Implements Paddle's webhook signature verification as documented at:
|
|
9
|
+
# https://developer.paddle.com/webhooks/signature-verification
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# verifier = PaddleRails::WebhookVerifier.new(secret_key)
|
|
13
|
+
# if verifier.verify(raw_body, signature_header)
|
|
14
|
+
# # Webhook is valid
|
|
15
|
+
# end
|
|
16
|
+
class WebhookVerifier
|
|
17
|
+
# Default tolerance for timestamp validation (5 seconds)
|
|
18
|
+
TIMESTAMP_TOLERANCE = 5
|
|
19
|
+
|
|
20
|
+
# @param secret_key [String] The webhook secret key from Paddle
|
|
21
|
+
# @param timestamp_tolerance [Integer] Maximum age of webhook in seconds (default: 5)
|
|
22
|
+
def initialize(secret_key, timestamp_tolerance: TIMESTAMP_TOLERANCE)
|
|
23
|
+
@secret_key = secret_key
|
|
24
|
+
@timestamp_tolerance = timestamp_tolerance
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Verify a webhook signature.
|
|
28
|
+
#
|
|
29
|
+
# @param raw_body [String] The raw request body (must not be transformed)
|
|
30
|
+
# @param signature_header [String] The value of the Paddle-Signature header
|
|
31
|
+
# @return [Boolean] true if signature is valid, false otherwise
|
|
32
|
+
def verify(raw_body, signature_header)
|
|
33
|
+
return false if raw_body.nil? || signature_header.nil? || @secret_key.nil?
|
|
34
|
+
|
|
35
|
+
timestamp, signatures = parse_signature_header(signature_header)
|
|
36
|
+
return false unless timestamp && signatures.any?
|
|
37
|
+
|
|
38
|
+
# Check timestamp to prevent replay attacks
|
|
39
|
+
return false unless timestamp_valid?(timestamp)
|
|
40
|
+
|
|
41
|
+
# Build signed payload: timestamp:raw_body
|
|
42
|
+
signed_payload = "#{timestamp}:#{raw_body}"
|
|
43
|
+
|
|
44
|
+
# Compute expected signature using HMAC-SHA256
|
|
45
|
+
expected_signature = compute_signature(signed_payload)
|
|
46
|
+
|
|
47
|
+
# Compare signatures (use timing-safe comparison)
|
|
48
|
+
signatures.any? { |sig| timing_safe_compare(sig, expected_signature) }
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
Rails.logger.error("PaddleRails::WebhookVerifier: Error verifying signature: #{e.message}")
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Parse the Paddle-Signature header.
|
|
57
|
+
#
|
|
58
|
+
# Format: "ts=1671552777;h1=eb4d0dc8853be92b7f063b9f3ba5233eb920a09459b6e6b2c26705b4364db151"
|
|
59
|
+
#
|
|
60
|
+
# @param signature_header [String] The Paddle-Signature header value
|
|
61
|
+
# @return [Array<Integer, Array<String>>] [timestamp, array_of_signatures]
|
|
62
|
+
def parse_signature_header(signature_header)
|
|
63
|
+
parts = signature_header.split(";")
|
|
64
|
+
timestamp = nil
|
|
65
|
+
signatures = []
|
|
66
|
+
|
|
67
|
+
parts.each do |part|
|
|
68
|
+
key, value = part.split("=", 2)
|
|
69
|
+
case key
|
|
70
|
+
when "ts"
|
|
71
|
+
timestamp = value.to_i
|
|
72
|
+
when "h1"
|
|
73
|
+
signatures << value
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
[timestamp, signatures]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Check if the timestamp is within the tolerance window.
|
|
81
|
+
#
|
|
82
|
+
# @param timestamp [Integer] Unix timestamp from the webhook
|
|
83
|
+
# @return [Boolean] true if timestamp is valid
|
|
84
|
+
def timestamp_valid?(timestamp)
|
|
85
|
+
current_time = Time.now.to_i
|
|
86
|
+
(current_time - timestamp).abs <= @timestamp_tolerance
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Compute HMAC-SHA256 signature for the signed payload.
|
|
90
|
+
#
|
|
91
|
+
# @param signed_payload [String] The timestamp:raw_body string
|
|
92
|
+
# @return [String] Hexadecimal signature
|
|
93
|
+
def compute_signature(signed_payload)
|
|
94
|
+
OpenSSL::HMAC.hexdigest("SHA256", @secret_key, signed_payload)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Timing-safe string comparison to prevent timing attacks.
|
|
98
|
+
#
|
|
99
|
+
# @param a [String] First string
|
|
100
|
+
# @param b [String] Second string
|
|
101
|
+
# @return [Boolean] true if strings are equal
|
|
102
|
+
def timing_safe_compare(a, b)
|
|
103
|
+
return false if a.nil? || b.nil?
|
|
104
|
+
return false unless a.length == b.length
|
|
105
|
+
|
|
106
|
+
OpenSSL.fixed_length_secure_compare(a, b)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
data/lib/paddle_rails.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# PaddleRails is a zero-hassle Paddle subscription integration for Rails
|
|
4
|
+
# using custom_data-based identity.
|
|
5
|
+
#
|
|
6
|
+
# @example Basic usage
|
|
7
|
+
# # In config/initializers/paddle_rails.rb
|
|
8
|
+
# PaddleRails.configure do |config|
|
|
9
|
+
# config.api_key = ENV["PADDLE_API_KEY"]
|
|
10
|
+
# config.subscription_owner_authenticator do
|
|
11
|
+
# current_user || warden.authenticate!(scope: :user)
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# @example Making a model subscribable
|
|
16
|
+
# class User < ApplicationRecord
|
|
17
|
+
# include PaddleRails::Subscribable
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# @see https://github.com/kjellberg/paddle_rails
|
|
21
|
+
module PaddleRails
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require "paddle"
|
|
25
|
+
require "paddle_rails/version"
|
|
26
|
+
require "paddle_rails/engine"
|
|
27
|
+
require "paddle_rails/configuration"
|
|
28
|
+
require "paddle_rails/product_sync"
|
|
29
|
+
require "paddle_rails/checkout"
|
|
30
|
+
require "paddle_rails/webhook_verifier"
|
|
31
|
+
require "paddle_rails/webhook_processor"
|
|
32
|
+
require "paddle_rails/subscription_sync"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Rake tasks for PaddleRails gem.
|
|
4
|
+
#
|
|
5
|
+
# @note This file currently contains placeholder tasks.
|
|
6
|
+
# Actual rake tasks will be added as needed.
|
|
7
|
+
#
|
|
8
|
+
# @example Running a task
|
|
9
|
+
# rake paddle_rails:sync_products
|
|
10
|
+
#
|
|
11
|
+
# @see PaddleRails::ProductSync
|
|
12
|
+
# desc "Explaining what the task does"
|
|
13
|
+
# task :paddle_rails do
|
|
14
|
+
# # Task goes here
|
|
15
|
+
# end
|
metadata
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: paddle_rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rasmus Kjellberg
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 8.1.1
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 8.1.1
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: paddle
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.6'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.6'
|
|
40
|
+
description: |
|
|
41
|
+
PaddleRails is a production-ready Rails engine that drops a complete subscription management portal into your application in minutes.
|
|
42
|
+
|
|
43
|
+
It's not just an API wrapper—it's a full-stack billing solution that handles the hard parts of SaaS payments: webhooks, plan upgrades, prorations, cancellation flows, and payment method updates. Fully compliant with Paddle Billing (v2), handling global tax/VAT and localized pricing automatically.
|
|
44
|
+
|
|
45
|
+
Features:
|
|
46
|
+
- Mountable billing dashboard ready in minutes
|
|
47
|
+
- Built for Paddle Billing V2
|
|
48
|
+
- Two-way sync via webhooks
|
|
49
|
+
- Beautiful UI built with Tailwind CSS
|
|
50
|
+
- Uses GlobalID for bulletproof user mapping (no email mismatch issues)
|
|
51
|
+
- Supports subscriptions on Users, Teams, Organizations, or Tenants
|
|
52
|
+
- Payment history with invoice viewing and download
|
|
53
|
+
- Plan upgrades/downgrades with proration
|
|
54
|
+
- Payment method management
|
|
55
|
+
|
|
56
|
+
No need to build billing UI from scratch. Just mount the engine and focus on your product.
|
|
57
|
+
email:
|
|
58
|
+
- 2277443+kjellberg@users.noreply.github.com
|
|
59
|
+
executables: []
|
|
60
|
+
extensions: []
|
|
61
|
+
extra_rdoc_files: []
|
|
62
|
+
files:
|
|
63
|
+
- MIT-LICENSE
|
|
64
|
+
- README.md
|
|
65
|
+
- Rakefile
|
|
66
|
+
- app/assets/stylesheets/paddle_rails/application.css
|
|
67
|
+
- app/assets/stylesheets/paddle_rails/tailwind.css
|
|
68
|
+
- app/assets/tailwind/application.css
|
|
69
|
+
- app/controllers/concerns/paddle_rails/paddle_checkout_error_handler.rb
|
|
70
|
+
- app/controllers/concerns/paddle_rails/subscription_owner.rb
|
|
71
|
+
- app/controllers/paddle_rails/application_controller.rb
|
|
72
|
+
- app/controllers/paddle_rails/checkout_controller.rb
|
|
73
|
+
- app/controllers/paddle_rails/dashboard_controller.rb
|
|
74
|
+
- app/controllers/paddle_rails/onboarding_controller.rb
|
|
75
|
+
- app/controllers/paddle_rails/payments_controller.rb
|
|
76
|
+
- app/controllers/paddle_rails/subscriptions_controller.rb
|
|
77
|
+
- app/controllers/paddle_rails/webhooks_controller.rb
|
|
78
|
+
- app/helpers/paddle_rails/application_helper.rb
|
|
79
|
+
- app/helpers/paddle_rails/subscription_owner_helper.rb
|
|
80
|
+
- app/jobs/paddle_rails/application_job.rb
|
|
81
|
+
- app/jobs/paddle_rails/process_webhook_job.rb
|
|
82
|
+
- app/mailers/paddle_rails/application_mailer.rb
|
|
83
|
+
- app/models/concerns/paddle_rails/subscribable.rb
|
|
84
|
+
- app/models/paddle_rails/application_record.rb
|
|
85
|
+
- app/models/paddle_rails/payment.rb
|
|
86
|
+
- app/models/paddle_rails/price.rb
|
|
87
|
+
- app/models/paddle_rails/product.rb
|
|
88
|
+
- app/models/paddle_rails/subscription.rb
|
|
89
|
+
- app/models/paddle_rails/subscription_item.rb
|
|
90
|
+
- app/models/paddle_rails/webhook_event.rb
|
|
91
|
+
- app/presenters/paddle_rails/payment_presenter.rb
|
|
92
|
+
- app/presenters/paddle_rails/product_presenter.rb
|
|
93
|
+
- app/presenters/paddle_rails/subscription_presenter.rb
|
|
94
|
+
- app/views/layouts/paddle_rails/application.html.erb
|
|
95
|
+
- app/views/paddle_rails/checkout/show.html.erb
|
|
96
|
+
- app/views/paddle_rails/dashboard/_change_plan.html.erb
|
|
97
|
+
- app/views/paddle_rails/dashboard/_current_subscription.html.erb
|
|
98
|
+
- app/views/paddle_rails/dashboard/_payment_history.html.erb
|
|
99
|
+
- app/views/paddle_rails/dashboard/_payment_method.html.erb
|
|
100
|
+
- app/views/paddle_rails/dashboard/show.html.erb
|
|
101
|
+
- app/views/paddle_rails/onboarding/show.html.erb
|
|
102
|
+
- app/views/paddle_rails/shared/configuration_error.html.erb
|
|
103
|
+
- config/routes.rb
|
|
104
|
+
- db/migrate/20251124180624_create_paddle_rails_subscription_plans.rb
|
|
105
|
+
- db/migrate/20251124180817_create_paddle_rails_subscription_prices.rb
|
|
106
|
+
- db/migrate/20251127221947_create_paddle_rails_webhook_events.rb
|
|
107
|
+
- db/migrate/20251128135831_create_paddle_rails_subscriptions.rb
|
|
108
|
+
- db/migrate/20251128142327_create_paddle_rails_subscription_items.rb
|
|
109
|
+
- db/migrate/20251128151334_remove_paddle_price_id_from_subscriptions.rb
|
|
110
|
+
- db/migrate/20251128151401_rename_subscription_plans_to_products.rb
|
|
111
|
+
- db/migrate/20251128151402_rename_subscription_plan_id_to_subscription_product_id.rb
|
|
112
|
+
- db/migrate/20251128151453_remove_subscription_price_id_from_subscriptions.rb
|
|
113
|
+
- db/migrate/20251128151501_add_subscription_product_id_to_subscription_items.rb
|
|
114
|
+
- db/migrate/20251128152025_remove_paddle_item_id_from_subscription_items.rb
|
|
115
|
+
- db/migrate/20251128212046_rename_subscription_products_to_products.rb
|
|
116
|
+
- db/migrate/20251128212047_rename_subscription_prices_to_prices.rb
|
|
117
|
+
- db/migrate/20251128212053_rename_subscription_product_id_to_product_id_in_prices.rb
|
|
118
|
+
- db/migrate/20251128212054_rename_fks_in_subscription_items.rb
|
|
119
|
+
- db/migrate/20251128220016_add_scheduled_cancelation_at_to_subscriptions.rb
|
|
120
|
+
- db/migrate/20251129121336_add_payment_method_to_subscriptions.rb
|
|
121
|
+
- db/migrate/20251129222345_create_paddle_rails_payments.rb
|
|
122
|
+
- lib/paddle_rails.rb
|
|
123
|
+
- lib/paddle_rails/checkout.rb
|
|
124
|
+
- lib/paddle_rails/configuration.rb
|
|
125
|
+
- lib/paddle_rails/engine.rb
|
|
126
|
+
- lib/paddle_rails/product_sync.rb
|
|
127
|
+
- lib/paddle_rails/subscription_sync.rb
|
|
128
|
+
- lib/paddle_rails/version.rb
|
|
129
|
+
- lib/paddle_rails/webhook_processor.rb
|
|
130
|
+
- lib/paddle_rails/webhook_verifier.rb
|
|
131
|
+
- lib/tasks/paddle_rails_tasks.rake
|
|
132
|
+
homepage: https://github.com/kjellberg/paddle_rails
|
|
133
|
+
licenses:
|
|
134
|
+
- MIT
|
|
135
|
+
metadata:
|
|
136
|
+
homepage_uri: https://github.com/kjellberg/paddle_rails
|
|
137
|
+
source_code_uri: https://github.com/kjellberg/paddle_rails
|
|
138
|
+
changelog_uri: https://github.com/kjellberg/paddle_rails/blob/main/CHANGELOG.md
|
|
139
|
+
rdoc_options: []
|
|
140
|
+
require_paths:
|
|
141
|
+
- lib
|
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
requirements: []
|
|
153
|
+
rubygems_version: 3.6.7
|
|
154
|
+
specification_version: 4
|
|
155
|
+
summary: Plug-and-play billing engine for Rails + Paddle. Full subscription management
|
|
156
|
+
portal with webhooks, plan changes, and payment history.
|
|
157
|
+
test_files: []
|