purchasekit 0.6.1 → 0.7.1

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: bfc4771e012ecaa84d79c4be751a4cca9f63ddb14629f4b6bc39bdace9d5ebb2
4
- data.tar.gz: 476cedaf6b9f2ea02692b80d19ba69e2ad1bcad739f49a060b8acd1f222c7bcd
3
+ metadata.gz: 82311c2fb6a6a3aea1ffe6997cfece956e4aef5544fa2ef3cb5e4fcb5a8266d8
4
+ data.tar.gz: aa2785f59d913cade15b3bd0c1335326279673c0a9e519af90fdc22d9de37e90
5
5
  SHA512:
6
- metadata.gz: b2573bd5d5d9b7e3ea17dfffc160fb430576ec70e6ed45ec12e93bedfa53c65e1a1d9e64a5eba0424cb97ed436538281af0720fe83e131bb61da602739b357d7
7
- data.tar.gz: 85d0a42f2fec7137008599ed367e35d2083419df92b673943a8d8a20fcb539c4390581abca537c00b2ef58c5de74be1924a1084a4800ab7d57476e9f6d4f5f20
6
+ metadata.gz: 752531ead05262da654b7069194df36af97cc4c9dd7fa2c496d230bf637e356ba4aebc97b1d02e44bccd37b34836fcaf432929c7fe46041bb518002979738d0c
7
+ data.tar.gz: 131b2ecb97d17134ff6ecea92162422bf82c9d14e7c938f526ba31c245e47bafdc5d14a394818260598b137e1588aea45981a5dcbe42ee86c6bed25a9d5f5340
data/README.md CHANGED
@@ -125,7 +125,36 @@ Webhooks may be delivered more than once. Write idempotent callbacks using `find
125
125
 
126
126
  ## Paywall helper
127
127
 
128
- Build a paywall using the included helper. Subscribe to the Turbo Stream for real-time redirects:
128
+ Build a paywall using the included helper. Subscribe to a Turbo Stream channel for real-time redirects after purchase. The `customer_id` you pass flows through the store and back to your webhook handler, so it must match what the handler expects.
129
+
130
+ ### With Pay
131
+
132
+ Pass the `Pay::Customer.id` (not your user ID), and subscribe to the Pay customer's `dom_id` channel:
133
+
134
+ ```erb
135
+ <% pay_customer = current_user.set_payment_processor(:purchasekit) %>
136
+
137
+ <%= turbo_stream_from dom_id(pay_customer) %>
138
+
139
+ <%= purchasekit_paywall customer_id: pay_customer.id, success_path: dashboard_path do |paywall| %>
140
+ <%= paywall.plan_option product: @annual, selected: true do %>
141
+ Annual - <%= paywall.price %>/year
142
+ <% end %>
143
+
144
+ <%= paywall.plan_option product: @monthly do %>
145
+ Monthly - <%= paywall.price %>/month
146
+ <% end %>
147
+
148
+ <%= paywall.submit "Subscribe" %>
149
+ <%= paywall.restore url: restore_purchases_path, class: "btn btn-link" %>
150
+ <% end %>
151
+ ```
152
+
153
+ `set_payment_processor(:purchasekit)` finds or creates the `Pay::Customer` row. Calling it on every paywall render guarantees the row exists before the webhook tries to look it up.
154
+
155
+ ### Without Pay
156
+
157
+ Use your own user ID for both the `customer_id` and the Turbo Stream channel:
129
158
 
130
159
  ```erb
131
160
  <%= turbo_stream_from "purchasekit_customer_#{current_user.id}" %>
@@ -19,7 +19,7 @@ module PurchaseKit
19
19
  subscription_name: "Demo Subscription",
20
20
  status: "active",
21
21
  current_period_start: Time.current.iso8601,
22
- current_period_end: 1.year.from_now.iso8601,
22
+ current_period_end: 1.month.from_now.iso8601,
23
23
  ends_at: nil,
24
24
  success_path: intent.success_path
25
25
  }
@@ -1,6 +1,8 @@
1
1
  module PurchaseKit
2
2
  class PurchasesController < ApplicationController
3
3
  def create
4
+ raise PurchaseKit::NotFoundError, "No product selected" if params[:product_id].blank?
5
+
4
6
  intent = PurchaseKit::Purchase::Intent.create(
5
7
  product_id: params[:product_id],
6
8
  customer_id: params[:customer_id],
@@ -2,12 +2,15 @@ module PurchaseKit
2
2
  module PaywallHelper
3
3
  # Renders a paywall form that triggers native in-app purchases
4
4
  #
5
- # @param customer_id [String, Integer] Your user/customer identifier
5
+ # @param customer_id [String, Integer] The identifier passed back in webhook
6
+ # events. With Pay, this must be `Pay::Customer.id` (the webhook handler
7
+ # does `Pay::Customer.find(customer_id)`). Without Pay, use your own user ID.
6
8
  # @param success_path [String] Where to redirect after successful purchase
7
9
  # @yield [PaywallBuilder] Builder for plan options and buttons
8
10
  #
9
- # Example:
10
- # <%= purchasekit_paywall customer_id: current_user.id, success_path: dashboard_path do |paywall| %>
11
+ # Example (with Pay):
12
+ # <% pay_customer = current_user.set_payment_processor(:purchasekit) %>
13
+ # <%= purchasekit_paywall customer_id: pay_customer.id, success_path: dashboard_path do |paywall| %>
11
14
  # <%= paywall.plan_option product: @annual, selected: true do %>
12
15
  # Annual - <%= paywall.price %>
13
16
  # <% end %>
@@ -48,6 +51,7 @@ module PurchaseKit
48
51
  selected,
49
52
  id: input_id,
50
53
  class: input_class,
54
+ required: true,
51
55
  autocomplete: "off",
52
56
  data: {
53
57
  purchasekit__paywall_target: "planRadio",
@@ -119,6 +119,11 @@ module PurchaseKit
119
119
  parse_time(payload[:ends_at])
120
120
  end
121
121
 
122
+ # When the trial period ends (nil if no trial)
123
+ def trial_ends_at
124
+ parse_time(payload[:trial_ends_at])
125
+ end
126
+
122
127
  # The success path you passed when creating the purchase intent.
123
128
  # Use this for redirecting after purchase completion.
124
129
  def success_path
@@ -21,6 +21,7 @@ module PurchaseKit
21
21
  quantity: 1,
22
22
  current_period_start: parse_time(event["current_period_start"]),
23
23
  current_period_end: parse_time(event["current_period_end"]),
24
+ trial_ends_at: parse_time(event["trial_ends_at"]),
24
25
  ends_at: parse_time(event["ends_at"]),
25
26
  data: (subscription.data || {}).merge("store" => event["store"])
26
27
  )
@@ -6,20 +6,31 @@ module PurchaseKit
6
6
  include Turbo::Streams::ActionHelper
7
7
 
8
8
  def call(event)
9
- update_subscription(event,
9
+ customer = ::Pay::Customer.find(event["customer_id"])
10
+
11
+ subscription = ::Pay::Purchasekit::Subscription.find_or_initialize_by(
12
+ customer: customer,
13
+ processor_id: event["subscription_id"]
14
+ )
15
+ subscription.name ||= event["subscription_name"] || ::Pay.default_product_name
16
+ subscription.quantity ||= 1
17
+
18
+ subscription.update!(
10
19
  processor_plan: event["store_product_id"],
11
20
  status: event["status"],
12
21
  current_period_start: parse_time(event["current_period_start"]),
13
22
  current_period_end: parse_time(event["current_period_end"]),
14
- ends_at: parse_time(event["ends_at"]))
23
+ trial_ends_at: parse_time(event["trial_ends_at"]),
24
+ ends_at: parse_time(event["ends_at"]),
25
+ data: (subscription.data || {}).merge("store" => event["store"])
26
+ )
15
27
 
16
- broadcast_redirect(event) if event["success_path"].present?
28
+ broadcast_redirect(customer, event) if event["success_path"].present?
17
29
  end
18
30
 
19
31
  private
20
32
 
21
- def broadcast_redirect(event)
22
- customer = ::Pay::Customer.find(event["customer_id"])
33
+ def broadcast_redirect(customer, event)
23
34
  Turbo::StreamsChannel.broadcast_stream_to(
24
35
  dom_id(customer),
25
36
  content: turbo_stream_action_tag(:redirect, url: event["success_path"])
@@ -1,3 +1,3 @@
1
1
  module PurchaseKit
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: purchasekit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Masilotti
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-03-15 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -225,7 +224,6 @@ metadata:
225
224
  homepage_uri: https://purchasekit.com
226
225
  source_code_uri: https://github.com/purchasekit/purchasekit
227
226
  changelog_uri: https://github.com/purchasekit/purchasekit/blob/main/CHANGELOG.md
228
- post_install_message:
229
227
  rdoc_options: []
230
228
  require_paths:
231
229
  - lib
@@ -240,8 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
240
238
  - !ruby/object:Gem::Version
241
239
  version: '0'
242
240
  requirements: []
243
- rubygems_version: 3.0.3.1
244
- signing_key:
241
+ rubygems_version: 4.0.3
245
242
  specification_version: 4
246
243
  summary: In-app purchase infrastructure for Rails
247
244
  test_files: []