purchasekit 0.3.2 → 0.4.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c18c744ebd13592ffe5eecc0a5766995e074c095442b3ecaf25e744c5eca26fd
4
- data.tar.gz: d560765f53f9b54947848d6292750622dea03c844e47498773e0d24f58794edb
3
+ metadata.gz: 2c7126b29d2fa71ef5635545def63c8f0fe533511f9860bfbff4f8a3db4cbf02
4
+ data.tar.gz: f98755ecec07759dfc6e8cdd2aba15276f56ef412846d7992598d750f23def3a
5
5
  SHA512:
6
- metadata.gz: 280c83c6afdcfdfacb9e036cc410513343d0816d752af4af436813a1fc6e389e0919705b8832ec588f0dbabd7a1bde5627aa98d29bb28698cf3895452a516c94
7
- data.tar.gz: 711a41d946b27e3cb4f067249767fb81ce436b7845d8bf3c0137f2faf00b4c8f4c8275cfdfddad97a2d4c97e10d0124cc239b8f336bda35f8738239e42f36566
6
+ metadata.gz: 0b0b9f59f3dab8d5bd0e9b8b858afc240f25279c4d07c760c13690459d58fd754637493aee9cd42d544fffff924e46067afbd12a3c999cfe9054ec1adcbbcd71
7
+ data.tar.gz: 70b940470d6c664da65e3d06023a646546e50528ffdf774d92c2ec6ad1efe7e2d9a0e1a6c26d32f3013149328b39f2819319ada21068aab885ddca45f69b3931
data/Rakefile CHANGED
@@ -1,10 +1,23 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
- Rake::TestTask.new(:test) do |t|
4
+ # Tests without Pay gem (ActiveSupport only, no Rails)
5
+ Rake::TestTask.new("test:standalone") do |t|
5
6
  t.libs << "test"
6
7
  t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
8
+ t.test_files = FileList["test/standalone/**/*_test.rb"]
9
+ t.warning = false
8
10
  end
9
11
 
12
+ # Tests with Pay gem integration
13
+ Rake::TestTask.new("test:pay") do |t|
14
+ t.libs << "test"
15
+ t.libs << "lib"
16
+ t.test_files = FileList["test/**/*_test.rb"].exclude("test/standalone/**/*_test.rb")
17
+ t.warning = false
18
+ end
19
+
20
+ desc "Run all tests (Standalone and Pay)"
21
+ task test: ["test:standalone", "test:pay"]
22
+
10
23
  task default: :test
@@ -24,7 +24,7 @@ module PurchaseKit
24
24
  purchasekit__paywall_customer_id_value: customer_id
25
25
  )
26
26
 
27
- form_with(url: purchasekit.purchases_path, id: "purchasekit_paywall", data: form_data, **options) do |form|
27
+ form_with(url: purchase_kit.purchases_path, id: "purchasekit_paywall", data: form_data, **options) do |form|
28
28
  hidden = hidden_field_tag(:customer_id, customer_id)
29
29
  hidden += hidden_field_tag(:success_path, success_path)
30
30
  hidden += hidden_field_tag(:environment, "sandbox", data: {purchasekit__paywall_target: "environment"})
@@ -1,3 +1,5 @@
1
+ require "purchasekit/pay/webhooks"
2
+
1
3
  module Pay
2
4
  module Purchasekit
3
5
  class Error < Pay::Error
@@ -1,3 +1,3 @@
1
1
  module PurchaseKit
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: purchasekit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Masilotti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-08 00:00:00.000000000 Z
11
+ date: 2026-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -58,6 +58,48 @@ dependencies:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '5.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: pay
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '11.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '11.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: propshaft
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: puma
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
61
103
  - !ruby/object:Gem::Dependency
62
104
  name: rake
63
105
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +114,34 @@ dependencies:
72
114
  - - "~>"
73
115
  - !ruby/object:Gem::Version
74
116
  version: '13.0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: sqlite3
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: turbo-rails
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
75
145
  - !ruby/object:Gem::Dependency
76
146
  name: vcr
77
147
  requirement: !ruby/object:Gem::Requirement
@@ -112,14 +182,9 @@ files:
112
182
  - README.md
113
183
  - Rakefile
114
184
  - app/controllers/purchase_kit/application_controller.rb
115
- - app/controllers/purchase_kit/pay/application_controller.rb
116
- - app/controllers/purchase_kit/pay/purchase/completions_controller.rb
117
- - app/controllers/purchase_kit/pay/purchases_controller.rb
118
- - app/controllers/purchase_kit/pay/webhooks_controller.rb
119
185
  - app/controllers/purchase_kit/purchase/completions_controller.rb
120
186
  - app/controllers/purchase_kit/purchases_controller.rb
121
187
  - app/controllers/purchase_kit/webhooks_controller.rb
122
- - app/helpers/purchase_kit/pay/paywall_helper.rb
123
188
  - app/helpers/purchase_kit/paywall_helper.rb
124
189
  - app/javascript/controllers/purchasekit/paywall_controller.js
125
190
  - app/javascript/purchasekit/turbo_actions.js
@@ -127,24 +192,17 @@ files:
127
192
  - app/models/pay/purchasekit/charge.rb
128
193
  - app/models/pay/purchasekit/customer.rb
129
194
  - app/models/pay/purchasekit/subscription.rb
130
- - app/views/purchase_kit/pay/purchases/_subscription_required.html.erb
131
- - app/views/purchase_kit/pay/purchases/create.turbo_stream.erb
132
195
  - app/views/purchase_kit/purchases/_error.html.erb
133
196
  - app/views/purchase_kit/purchases/_intent.html.erb
134
197
  - config/importmap.rb
135
198
  - config/routes.rb
136
199
  - lib/pay/purchasekit.rb
137
- - lib/purchasekit-pay.rb
138
200
  - lib/purchasekit.rb
139
201
  - lib/purchasekit/api_client.rb
140
202
  - lib/purchasekit/configuration.rb
141
203
  - lib/purchasekit/engine.rb
142
204
  - lib/purchasekit/error.rb
143
205
  - lib/purchasekit/events.rb
144
- - lib/purchasekit/pay/configuration.rb
145
- - lib/purchasekit/pay/engine.rb
146
- - lib/purchasekit/pay/error.rb
147
- - lib/purchasekit/pay/version.rb
148
206
  - lib/purchasekit/pay/webhook.rb
149
207
  - lib/purchasekit/pay/webhooks.rb
150
208
  - lib/purchasekit/pay/webhooks/base.rb
@@ -1,2 +0,0 @@
1
- class PurchaseKit::Pay::ApplicationController < ActionController::Base
2
- end
@@ -1,32 +0,0 @@
1
- class PurchaseKit::Pay::Purchase::CompletionsController < PurchaseKit::Pay::ApplicationController
2
- include ActionView::RecordIdentifier
3
- include Turbo::Streams::ActionHelper
4
-
5
- skip_before_action :verify_authenticity_token
6
-
7
- def create
8
- unless PurchaseKit::Pay.config.demo_mode?
9
- head :not_found
10
- return
11
- end
12
-
13
- intent = PurchaseKit::Purchase::Intent::Demo.find(params[:intent_uuid])
14
- customer = ::Pay::Customer.find(intent.customer_id)
15
-
16
- customer.subscriptions.create!(
17
- name: "default",
18
- processor_id: "demo_#{SecureRandom.hex(12)}",
19
- processor_plan: intent.product.apple_product_id,
20
- status: "active",
21
- quantity: 1
22
- )
23
-
24
- redirect_path = intent.success_path || "/"
25
- Turbo::StreamsChannel.broadcast_stream_to(
26
- dom_id(customer),
27
- content: turbo_stream_action_tag(:redirect, url: redirect_path)
28
- )
29
-
30
- head :ok
31
- end
32
- end
@@ -1,34 +0,0 @@
1
- class PurchaseKit::Pay::PurchasesController < PurchaseKit::Pay::ApplicationController
2
- rescue_from PurchaseKit::Pay::SubscriptionRequiredError, with: :subscription_required
3
-
4
- def create
5
- @customer = ::Pay::Customer.find(params[:customer_id])
6
-
7
- @intent = PurchaseKit::Purchase::Intent.create(
8
- product_id: params[:product_id],
9
- customer_id: @customer.id,
10
- success_path: params[:success_path],
11
- environment: params[:environment]
12
- )
13
-
14
- @xcode_completion_url = PurchaseKit::Pay.config.xcode_completion_url(intent_uuid: @intent.uuid, host: request.base_url)
15
-
16
- respond_to do |format|
17
- format.turbo_stream
18
- end
19
- end
20
-
21
- private
22
-
23
- def subscription_required(exception)
24
- respond_to do |format|
25
- format.turbo_stream do
26
- render turbo_stream: turbo_stream.replace(
27
- "purchasekit_paywall",
28
- partial: "purchase_kit/pay/purchases/subscription_required",
29
- locals: {message: exception.message}
30
- )
31
- end
32
- end
33
- end
34
- end
@@ -1,35 +0,0 @@
1
- class PurchaseKit::Pay::WebhooksController < PurchaseKit::Pay::ApplicationController
2
- skip_forgery_protection
3
-
4
- def create
5
- PurchaseKit::Pay::Webhook.queue(verified_event)
6
- head :ok
7
- rescue SignatureVerificationError => e
8
- Rails.logger.error "[PurchaseKit] Webhook signature error: #{e.message}"
9
- head :bad_request
10
- end
11
-
12
- private
13
-
14
- def verified_event
15
- payload = request.raw_post
16
- signature = request.headers["X-PurchaseKit-Signature"]
17
- secret = PurchaseKit::Pay.config.webhook_secret
18
-
19
- if secret.blank?
20
- raise SignatureVerificationError, "webhook_secret must be configured" if Rails.env.production?
21
- return JSON.parse(payload, symbolize_names: true)
22
- end
23
-
24
- raise SignatureVerificationError, "Missing signature" if signature.blank?
25
-
26
- expected = OpenSSL::HMAC.hexdigest("SHA256", secret, payload)
27
- unless ActiveSupport::SecurityUtils.secure_compare(signature, expected)
28
- raise SignatureVerificationError, "Invalid signature"
29
- end
30
-
31
- JSON.parse(payload, symbolize_names: true)
32
- end
33
-
34
- class SignatureVerificationError < StandardError; end
35
- end
@@ -1,78 +0,0 @@
1
- module PurchaseKit::Pay::PaywallHelper
2
- # Wrapper for paywall with bridge controller
3
- # Renders a form that posts to the purchases endpoint
4
- # Yields a builder for plan options and buttons
5
- # Requires a Pay::Customer - call user.set_payment_processor(:purchasekit) first
6
- def purchasekit_paywall(customer:, success_path: main_app.root_path, **options)
7
- raise ArgumentError, "Must provide customer: parameter. Call user.set_payment_processor(:purchasekit) first." unless customer
8
-
9
- builder = PurchaseKit::Pay::PaywallBuilder.new(self, customer)
10
-
11
- form_data = (options.delete(:data) || {}).merge(
12
- controller: "purchasekit--paywall",
13
- purchasekit__paywall_customer_id_value: customer.id
14
- )
15
-
16
- form_with(url: purchasekit_pay.purchases_path, id: "purchasekit_paywall", data: form_data, **options) do |form|
17
- hidden = hidden_field_tag(:customer_id, customer.id)
18
- hidden += hidden_field_tag(:success_path, success_path)
19
- hidden += hidden_field_tag(:environment, "sandbox", data: {purchasekit__paywall_target: "environment"})
20
- hidden + capture { yield builder }
21
- end
22
- end
23
- end
24
-
25
- class PurchaseKit::Pay::PaywallBuilder
26
- def initialize(template, customer)
27
- @template = template
28
- @customer = customer
29
- @current_product = nil
30
- end
31
-
32
- def plan_option(product:, selected: false, input_class: nil, **options, &block)
33
- input_id = "purchasekit_plan_#{product.id.parameterize.underscore}"
34
-
35
- radio = @template.radio_button_tag(
36
- :product_id,
37
- product.id,
38
- selected,
39
- id: input_id,
40
- class: input_class,
41
- autocomplete: "off",
42
- data: {
43
- purchasekit__paywall_target: "planRadio",
44
- apple_store_product_id: product.apple_product_id,
45
- google_store_product_id: product.google_product_id
46
- }
47
- )
48
-
49
- @current_product = product
50
- label = @template.label_tag(input_id, **options) { @template.capture(&block) }
51
- @current_product = nil
52
-
53
- radio + label
54
- end
55
-
56
- def price(**options, &block)
57
- raise "price must be called within a plan_option block" unless @current_product
58
-
59
- data = (options.delete(:data) || {}).merge(
60
- purchasekit__paywall_target: "price",
61
- apple_store_product_id: @current_product.apple_product_id,
62
- google_store_product_id: @current_product.google_product_id
63
- )
64
-
65
- loading_content = block ? @template.capture(&block) : "Loading..."
66
- @template.content_tag(:span, loading_content, data: data, **options)
67
- end
68
-
69
- def submit(text = "Subscribe", **options)
70
- data = (options.delete(:data) || {}).merge(
71
- purchasekit__paywall_target: "submitButton",
72
- turbo_submits_with: text
73
- )
74
-
75
- @template.submit_tag(text, disabled: true, data: data, **options)
76
- end
77
-
78
- end
@@ -1,3 +0,0 @@
1
- <div id="purchasekit_paywall" class="purchasekit-error">
2
- <p><%= message %></p>
3
- </div>
@@ -1,8 +0,0 @@
1
- <%= turbo_stream.append "purchasekit_paywall" do %>
2
- <div data-purchasekit--paywall-target="response"
3
- data-correlation-id="<%= @intent.uuid %>"
4
- data-apple-store-product-id="<%= @intent.product.apple_product_id %>"
5
- data-google-store-product-id="<%= @intent.product.google_product_id %>"
6
- data-xcode-completion-url="<%= @xcode_completion_url %>">
7
- </div>
8
- <% end %>
@@ -1,42 +0,0 @@
1
- module PurchaseKit
2
- module Pay
3
- class Configuration
4
- attr_accessor :api_key, :api_url, :app_id, :webhook_secret
5
- attr_accessor :demo_mode, :demo_products
6
-
7
- def initialize
8
- @api_url = "https://purchasekit.dev"
9
- @demo_mode = false
10
- @demo_products = {}
11
- end
12
-
13
- def demo_mode?
14
- @demo_mode
15
- end
16
-
17
- def base_api_url
18
- "#{api_url}/api/v1"
19
- end
20
-
21
- def xcode_completion_url(intent_uuid:, host: nil)
22
- if demo_mode?
23
- PurchaseKit::Pay::Engine.routes.url_helpers.purchase_intent_completions_url(intent_uuid: intent_uuid, host: host)
24
- else
25
- "#{base_api_url}/purchase/intents/#{intent_uuid}/completions"
26
- end
27
- end
28
- end
29
-
30
- class << self
31
- attr_writer :config
32
-
33
- def config
34
- @config ||= Configuration.new
35
- end
36
-
37
- def configure
38
- yield(config)
39
- end
40
- end
41
- end
42
- end
@@ -1,35 +0,0 @@
1
- module PurchaseKit
2
- module Pay
3
- class Engine < ::Rails::Engine
4
- isolate_namespace PurchaseKit::Pay
5
-
6
- initializer "purchasekit_pay.register_processor", before: :load_config_initializers do
7
- ::Pay.enabled_processors << :purchasekit unless ::Pay.enabled_processors.include?(:purchasekit)
8
- end
9
-
10
- initializer "purchasekit_pay.webhooks", after: :load_config_initializers do
11
- ::Pay::Purchasekit.configure_webhooks
12
- end
13
-
14
- initializer "purchasekit_pay.helpers" do
15
- ActiveSupport.on_load(:action_controller_base) do
16
- helper PurchaseKit::Pay::PaywallHelper
17
- end
18
- end
19
-
20
- initializer "purchasekit_pay.importmap", before: "importmap" do |app|
21
- if app.config.respond_to?(:importmap)
22
- app.config.importmap.paths << Engine.root.join("config/importmap.rb")
23
- app.config.importmap.cache_sweepers << root.join("app/javascript")
24
- end
25
- end
26
-
27
- initializer "purchasekit_pay.assets" do |app|
28
- if app.config.respond_to?(:assets) && app.config.assets.respond_to?(:paths)
29
- app.config.assets.paths << root.join("app/javascript")
30
- app.config.assets.precompile += %w[purchasekit-pay/manifest.js]
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,12 +0,0 @@
1
- module PurchaseKit
2
- module Pay
3
- class Error < ::Pay::Error
4
- end
5
-
6
- class NotFoundError < Error
7
- end
8
-
9
- class SubscriptionRequiredError < Error
10
- end
11
- end
12
- end
@@ -1,5 +0,0 @@
1
- module PurchaseKit
2
- module Pay
3
- VERSION = "0.1.4"
4
- end
5
- end
@@ -1,15 +0,0 @@
1
- require "pay"
2
- require "purchasekit/pay/version"
3
- require "purchasekit/pay/configuration"
4
- require "purchasekit/pay/error"
5
- require "purchasekit/pay/engine"
6
- require "purchasekit/pay/webhooks"
7
- require "purchasekit/pay/webhook"
8
- require "purchasekit/api_client"
9
- require "purchasekit/product"
10
- require "purchasekit/product/demo"
11
- require "purchasekit/product/remote"
12
- require "purchasekit/purchase/intent"
13
- require "purchasekit/purchase/intent/demo"
14
- require "purchasekit/purchase/intent/remote"
15
- require "pay/purchasekit"