purchasekit 0.8.0 → 0.9.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c4b7e933f67b6bb8b2d52dce69e7897d9e6834c58f593a44eb6c27629228d8fa
|
|
4
|
+
data.tar.gz: 2ff18e42f762b9613aedebbe3a05a12e0cb127cb425f4cce0d79c1010bcfdf3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eed594dc1d832cd950327956578f421598d68395c353d1aa3f1790d635ab744ca3fc8573d7888a5a1b34d8d19ba0308b929ed3eb215d2ca53fef001edb988520
|
|
7
|
+
data.tar.gz: 470631898b0a2b6872903299a2e4c947bac402c30eaea904eeb5eaa029c77d66f9f6878bfaf0fd485e842e6e62d2eeaaeec452e6f9e4dab46d6c4ec8be4982a8
|
|
@@ -6,6 +6,11 @@ module PurchaseKit
|
|
|
6
6
|
# events. With Pay, this must be `Pay::Customer.id` (the webhook handler
|
|
7
7
|
# does `Pay::Customer.find(customer_id)`). Without Pay, use your own user ID.
|
|
8
8
|
# @param success_path [String] Where to redirect after successful purchase
|
|
9
|
+
# @param proration_mode [String] Google Play replacement mode used when a base
|
|
10
|
+
# plan is swapped within one umbrella subscription (e.g. monthly to annual).
|
|
11
|
+
# One of "charge_prorated_price" (default), "with_time_proration",
|
|
12
|
+
# "charge_full_price", "without_proration", or "deferred". Ignored on Apple,
|
|
13
|
+
# which handles intra-group upgrades and downgrades automatically.
|
|
9
14
|
# @yield [PaywallBuilder] Builder for plan options and buttons
|
|
10
15
|
#
|
|
11
16
|
# Example (with Pay):
|
|
@@ -17,14 +22,15 @@ module PurchaseKit
|
|
|
17
22
|
# <%= paywall.submit "Subscribe" %>
|
|
18
23
|
# <% end %>
|
|
19
24
|
#
|
|
20
|
-
def purchasekit_paywall(customer_id:, success_path: main_app.root_path, **options)
|
|
25
|
+
def purchasekit_paywall(customer_id:, success_path: main_app.root_path, proration_mode: "charge_prorated_price", **options)
|
|
21
26
|
raise ArgumentError, "customer_id is required" if customer_id.blank?
|
|
22
27
|
|
|
23
28
|
builder = PaywallBuilder.new(self)
|
|
24
29
|
|
|
25
30
|
form_data = (options.delete(:data) || {}).merge(
|
|
26
31
|
controller: "purchasekit--paywall",
|
|
27
|
-
purchasekit__paywall_customer_id_value: customer_id
|
|
32
|
+
purchasekit__paywall_customer_id_value: customer_id,
|
|
33
|
+
purchasekit__paywall_proration_mode_value: proration_mode
|
|
28
34
|
)
|
|
29
35
|
|
|
30
36
|
form_with(url: purchase_kit.purchases_path, id: "purchasekit_paywall", data: form_data, **options) do |form|
|
|
@@ -3,6 +3,7 @@ import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
|
|
|
3
3
|
export default class extends BridgeComponent {
|
|
4
4
|
static component = "paywall"
|
|
5
5
|
static targets = ["planRadio", "price", "submitButton", "response", "environment", "restoreButton"]
|
|
6
|
+
static values = { prorationMode: { type: String, default: "charge_prorated_price" } }
|
|
6
7
|
|
|
7
8
|
connect() {
|
|
8
9
|
super.connect()
|
|
@@ -13,6 +14,7 @@ export default class extends BridgeComponent {
|
|
|
13
14
|
if (this.#fallbackTimeoutId) {
|
|
14
15
|
clearTimeout(this.#fallbackTimeoutId)
|
|
15
16
|
}
|
|
17
|
+
this.#stopWatchingForCompletion()
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
restore() {
|
|
@@ -53,11 +55,14 @@ export default class extends BridgeComponent {
|
|
|
53
55
|
|
|
54
56
|
element.remove()
|
|
55
57
|
this.#disableForm()
|
|
58
|
+
this.dispatch("initiated", { detail: { correlationId } })
|
|
56
59
|
this.#triggerNativePurchase(productIds, correlationId, xcodeCompletionUrl, successPath)
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
#triggerNativePurchase(productIds, correlationId, xcodeCompletionUrl, successPath) {
|
|
60
|
-
|
|
63
|
+
// googleStoreProrationMode only applies on Android plan swaps; iOS ignores it.
|
|
64
|
+
const googleStoreProrationMode = this.prorationModeValue
|
|
65
|
+
this.send("purchase", { ...productIds, correlationId, xcodeCompletionUrl, googleStoreProrationMode }, message => {
|
|
61
66
|
const { status, error } = message.data
|
|
62
67
|
|
|
63
68
|
if (error) {
|
|
@@ -72,16 +77,53 @@ export default class extends BridgeComponent {
|
|
|
72
77
|
return
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
window.Turbo.visit(successPath)
|
|
80
|
-
}, 30000)
|
|
81
|
-
}
|
|
80
|
+
// The store confirmed the purchase. The Pay::Subscription (and the redirect)
|
|
81
|
+
// still depend on the server webhook, so move into the awaiting state.
|
|
82
|
+
this.dispatch("store-confirmed", { detail: { status } })
|
|
83
|
+
this.#awaitCompletion(successPath)
|
|
82
84
|
})
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
// Waits for the webhook-driven redirect after the store confirms a purchase.
|
|
88
|
+
// The redirect arrives as a Turbo Stream "redirect" action over ActionCable, with
|
|
89
|
+
// a 30-second fallback for when ActionCable isn't connected.
|
|
90
|
+
#awaitCompletion(successPath) {
|
|
91
|
+
this.dispatch("awaiting-webhook", { detail: {} })
|
|
92
|
+
|
|
93
|
+
this.#streamRenderListener = event => {
|
|
94
|
+
if (event.target?.getAttribute("action") === "redirect") {
|
|
95
|
+
this.#complete()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
document.addEventListener("turbo:before-stream-render", this.#streamRenderListener)
|
|
99
|
+
|
|
100
|
+
if (successPath) {
|
|
101
|
+
this.#fallbackTimeoutId = setTimeout(() => {
|
|
102
|
+
this.#complete()
|
|
103
|
+
window.Turbo.visit(successPath)
|
|
104
|
+
}, 30000)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#complete() {
|
|
109
|
+
if (this.#completed) return
|
|
110
|
+
this.#completed = true
|
|
111
|
+
|
|
112
|
+
if (this.#fallbackTimeoutId) {
|
|
113
|
+
clearTimeout(this.#fallbackTimeoutId)
|
|
114
|
+
this.#fallbackTimeoutId = null
|
|
115
|
+
}
|
|
116
|
+
this.#stopWatchingForCompletion()
|
|
117
|
+
this.dispatch("complete", { detail: {} })
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#stopWatchingForCompletion() {
|
|
121
|
+
if (this.#streamRenderListener) {
|
|
122
|
+
document.removeEventListener("turbo:before-stream-render", this.#streamRenderListener)
|
|
123
|
+
this.#streamRenderListener = null
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
85
127
|
#submitRestore(url, subscriptionIds) {
|
|
86
128
|
const csrfToken = document.querySelector("meta[name=csrf-token]")?.content
|
|
87
129
|
|
|
@@ -103,6 +145,8 @@ export default class extends BridgeComponent {
|
|
|
103
145
|
}
|
|
104
146
|
|
|
105
147
|
#fallbackTimeoutId = null
|
|
148
|
+
#streamRenderListener = null
|
|
149
|
+
#completed = false
|
|
106
150
|
|
|
107
151
|
#fetchPrices() {
|
|
108
152
|
const products = this.priceTargets.map(el => this.#productIds(el))
|
data/lib/purchasekit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: purchasekit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joe Masilotti
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-06-15 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: rails
|
|
@@ -224,6 +225,7 @@ metadata:
|
|
|
224
225
|
homepage_uri: https://purchasekit.com
|
|
225
226
|
source_code_uri: https://github.com/purchasekit/purchasekit
|
|
226
227
|
changelog_uri: https://github.com/purchasekit/purchasekit/blob/main/CHANGELOG.md
|
|
228
|
+
post_install_message:
|
|
227
229
|
rdoc_options: []
|
|
228
230
|
require_paths:
|
|
229
231
|
- lib
|
|
@@ -238,7 +240,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
238
240
|
- !ruby/object:Gem::Version
|
|
239
241
|
version: '0'
|
|
240
242
|
requirements: []
|
|
241
|
-
rubygems_version:
|
|
243
|
+
rubygems_version: 3.2.3
|
|
244
|
+
signing_key:
|
|
242
245
|
specification_version: 4
|
|
243
246
|
summary: In-app purchase infrastructure for Rails
|
|
244
247
|
test_files: []
|