belt-pay 0.0.5 → 0.0.6

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: 937d9e1a7ca86c274b1dd20feb2ad16b2f9f9aa8544b0bffd6d72ed1550768f7
4
- data.tar.gz: f33e95b73bd5f8104d3da45c34ce7e3e4e6ca329224721c80271af3f8e7c85d3
3
+ metadata.gz: df0a213a964e2b9d6e20019719612a03ae5a38fdafaade9f75bdb8b853f2ca87
4
+ data.tar.gz: 0a539c50414142e25667909c2858a45a00056e9afeb6b5f3ed824d50d7b50e30
5
5
  SHA512:
6
- metadata.gz: b1c7f6bb3f97548a05e060090f47b586aaf0435237931cd1c67b55534efd72eb706cf6af1d7294f07a5210268fdd6c232ef951d410722b00de1bdc56f43c30cc
7
- data.tar.gz: 73e0341c2f2441a279814645e959df9f1b94c41d88ba70018bac6b3ef10c144583288e945ee0a1d22e955c057361dc5e71858c5e4a63769e9a2fb47af3d4ad38
6
+ metadata.gz: a8ece6c8fc7785233a665207bb5d68a7f652e3097d1706dd18fcb614b0b049a14d71b06f161fcda0acbd706ce01617061465c92b332f34c7f1f77021d2dafdd2
7
+ data.tar.gz: 5fae3025eff2bbc50cac637a22621d1f4de3b259a487653f5a3aeb2b6fe062312b6c4f9740088caa42288cc17d8b327bebaf698bc51fa819dc63a6d3767a98f4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.6 — 2026-11-02
4
+
5
+ - **`pay:setup` works with a restricted runtime key.** The stored key is what the app
6
+ runs with, so it should be a restricted key (`rk_`) holding only what checkout and the
7
+ portal need. Setup used to manage the webhook endpoint with that same key, so a
8
+ restricted key without Webhook Endpoints access failed with a Stripe permission error.
9
+ When Stripe refuses the stored key, setup now asks once for a key that can manage
10
+ endpoints (an `sk_` secret key, or a restricted key with Webhook Endpoints: Write),
11
+ uses it for the endpoint list/create/delete calls in that run only, and never stores
12
+ it. A key from the other Stripe mode is refused. Leaving the prompt blank, or a second
13
+ refusal, falls back to the Dashboard steps; the stored key is still saved either way.
14
+
3
15
  ## 0.0.5 — 2026-10-31
4
16
 
5
17
  - **`create_checkout` no longer drops Checkout Session options.** The Stripe provider used
data/README.md CHANGED
@@ -200,6 +200,13 @@ either prompt blank to preserve its existing value. Use `--webhook-url` /
200
200
  `BELT_PAY_WEBHOOK_URL` or `--webhook-events` / `BELT_PAY_WEBHOOK_EVENTS` to override the
201
201
  defaults. Test (`sk_test_`) and live (`sk_live_`) keys get separate endpoints and secrets.
202
202
 
203
+ Prefer a [restricted key](https://docs.stripe.com/keys#limit-access) (`rk_`) as the
204
+ stored key, scoped to what your app calls at runtime (typically Customers, Checkout
205
+ Sessions, Customer portal and Subscriptions). It doesn't need Webhook Endpoints access:
206
+ when Stripe refuses it for the endpoint step, setup asks once for a key that can manage
207
+ endpoints (your `sk_` secret key, or a restricted key with Webhook Endpoints: Write),
208
+ uses it for that step only, and doesn't store it.
209
+
203
210
  You can also pass the environment with standard Rake task-argument syntax:
204
211
 
205
212
  ```bash
data/lib/belt/pay/cli.rb CHANGED
@@ -14,6 +14,7 @@ module Belt
14
14
  module Pay
15
15
  class CLI
16
16
  class Error < StandardError; end
17
+ class EndpointKeyUnavailable < StandardError; end
17
18
 
18
19
  COMMANDS = %w[secrets:setup secrets:show secrets:edit].freeze
19
20
  WEBHOOK_PATH = '/pay/webhooks'
@@ -127,7 +128,7 @@ module Belt
127
128
  updated['stripe_webhook_secret'] = webhook_secret unless blank?(webhook_secret)
128
129
 
129
130
  update_secret(response, original, updated)
130
- delete_replaced_endpoints(updated['stripe_secret_key'], replaced)
131
+ delete_replaced_endpoints(replaced)
131
132
  end
132
133
 
133
134
  # Returns [signing_secret, endpoints_to_delete]. Offers to create the Stripe
@@ -147,7 +148,8 @@ module Belt
147
148
  create = !url.nil? && confirm("Create the #{mode}-mode webhook endpoint in Stripe now?", default: blank?(existing_secret))
148
149
  return [manual_webhook_secret(url, mode: mode), []] unless create
149
150
 
150
- existing = stripe_endpoints(api_key).select { |endpoint| endpoint.url == url }
151
+ @endpoint_key = api_key
152
+ existing = with_endpoint_key(mode) { |key| stripe_endpoints(key) }.select { |endpoint| endpoint.url == url }
151
153
  if existing.any?
152
154
  ids = existing.map(&:id).join(', ')
153
155
  @out.puts "Stripe already has an endpoint for this URL (#{ids}). Stripe never re-sends a signing secret,"
@@ -158,12 +160,17 @@ module Belt
158
160
  end
159
161
  end
160
162
 
161
- endpoint = ::Stripe::WebhookEndpoint.create(
162
- { url: url, enabled_events: webhook_events, description: "Belt Pay (#{@app_name} #{@environment})" },
163
- { api_key: api_key }
164
- )
163
+ endpoint = with_endpoint_key(mode) do |key|
164
+ ::Stripe::WebhookEndpoint.create(
165
+ { url: url, enabled_events: webhook_events, description: "Belt Pay (#{@app_name} #{@environment})" },
166
+ { api_key: key }
167
+ )
168
+ end
165
169
  @out.puts "Created #{mode}-mode webhook endpoint #{endpoint.id} for #{webhook_events.size} events."
166
170
  [endpoint.secret, existing.map(&:id)]
171
+ rescue EndpointKeyUnavailable => e
172
+ @out.puts e.message
173
+ [manual_webhook_secret(url, mode: mode), []]
167
174
  end
168
175
 
169
176
  def manual_webhook_secret(url, mode: nil)
@@ -183,13 +190,38 @@ module Belt
183
190
  hidden_prompt('Stripe webhook signing secret [blank keeps existing]')
184
191
  end
185
192
 
186
- def delete_replaced_endpoints(api_key, ids)
193
+ def delete_replaced_endpoints(ids)
187
194
  ids.each do |id|
188
- ::Stripe::WebhookEndpoint.delete(id, {}, { api_key: api_key })
195
+ ::Stripe::WebhookEndpoint.delete(id, {}, { api_key: @endpoint_key })
189
196
  @out.puts "Deleted replaced webhook endpoint #{id}"
190
197
  end
191
198
  end
192
199
 
200
+ # The stored key is what the app runs with, so it is usually a restricted key
201
+ # (rk_) without Webhook Endpoints access. When Stripe refuses it, ask once for a
202
+ # key that can manage endpoints (an sk_ secret key, or a restricted key with
203
+ # Webhook Endpoints: Write), use it for the endpoint calls in this run only, and
204
+ # never store it.
205
+ def with_endpoint_key(mode)
206
+ yield @endpoint_key
207
+ rescue ::Stripe::PermissionError => e
208
+ raise EndpointKeyUnavailable, "Stripe refused that key too: #{e.message}" if @endpoint_key_borrowed
209
+
210
+ @out.puts 'The stored Stripe key cannot manage webhook endpoints (a restricted key usually cannot).'
211
+ @out.puts 'Setup can use a second key for this step only; it is not stored.'
212
+ key = hidden_prompt("Stripe #{mode} secret key, or restricted key with Webhook Endpoints write [not stored]")
213
+ raise EndpointKeyUnavailable, 'No key given.' if blank?(key)
214
+
215
+ key = key.strip
216
+ unless stripe_mode(key) == mode
217
+ raise EndpointKeyUnavailable, "That is a #{stripe_mode(key)}-mode key; the stored key is #{mode} mode."
218
+ end
219
+
220
+ @endpoint_key = key
221
+ @endpoint_key_borrowed = true
222
+ retry
223
+ end
224
+
193
225
  def stripe_endpoints(api_key)
194
226
  ::Stripe::WebhookEndpoint.list({ limit: 100 }, { api_key: api_key }).auto_paging_each.to_a
195
227
  end
@@ -419,7 +451,8 @@ module Belt
419
451
 
420
452
  Security:
421
453
  secrets:setup reads values without echo and sends them directly through the
422
- AWS SDK. secrets:show writes the complete secret to stdout. secrets:edit uses
454
+ AWS SDK. If the stored key is a restricted key without Webhook Endpoints
455
+ access, it asks for a second key for the endpoint step and never stores it. secrets:show writes the complete secret to stdout. secrets:edit uses
423
456
  a mode-0600 temporary file, validates JSON, and removes the file afterward.
424
457
  HELP
425
458
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Belt
4
4
  module Pay
5
- VERSION = '0.0.5'
5
+ VERSION = '0.0.6'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belt-pay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla