@fantastic.dev/better-stripe 0.11.0 → 0.13.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.
- package/CHANGELOG.md +30 -0
- package/README.md +267 -2
- package/dist/client/billing/balanceFunding.d.ts +36 -0
- package/dist/client/billing/balanceFunding.d.ts.map +1 -0
- package/dist/client/billing/balanceFunding.js +107 -0
- package/dist/client/billing/balanceFunding.js.map +1 -0
- package/dist/client/billing/invoicePreview.d.ts +28 -0
- package/dist/client/billing/invoicePreview.d.ts.map +1 -0
- package/dist/client/billing/invoicePreview.js +56 -0
- package/dist/client/billing/invoicePreview.js.map +1 -0
- package/dist/client/charge-currency-format.d.ts +3 -0
- package/dist/client/charge-currency-format.d.ts.map +1 -0
- package/dist/client/charge-currency-format.js +22 -0
- package/dist/client/charge-currency-format.js.map +1 -0
- package/dist/client/charge-currency.d.ts +14 -0
- package/dist/client/charge-currency.d.ts.map +1 -0
- package/dist/client/charge-currency.js +85 -0
- package/dist/client/charge-currency.js.map +1 -0
- package/dist/client/costRecovery-invoices.d.ts +38 -0
- package/dist/client/costRecovery-invoices.d.ts.map +1 -0
- package/dist/client/costRecovery-invoices.js +105 -0
- package/dist/client/costRecovery-invoices.js.map +1 -0
- package/dist/client/costRecovery.d.ts +156 -0
- package/dist/client/costRecovery.d.ts.map +1 -0
- package/dist/client/costRecovery.js +117 -0
- package/dist/client/costRecovery.js.map +1 -0
- package/dist/client/index.d.ts +28 -66
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +25 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/products/prices.d.ts +3 -2
- package/dist/client/products/prices.d.ts.map +1 -1
- package/dist/client/products/prices.js +0 -3
- package/dist/client/products/prices.js.map +1 -1
- package/dist/client/products/products.d.ts +3 -2
- package/dist/client/products/products.d.ts.map +1 -1
- package/dist/client/products/products.js +0 -3
- package/dist/client/products/products.js.map +1 -1
- package/dist/client/settlement/processingCosts.d.ts +56 -0
- package/dist/client/settlement/processingCosts.d.ts.map +1 -0
- package/dist/client/settlement/processingCosts.js +173 -0
- package/dist/client/settlement/processingCosts.js.map +1 -0
- package/dist/client/utils/webhookEndpoints.d.ts +1 -1
- package/dist/client/utils/webhookEndpoints.d.ts.map +1 -1
- package/dist/client/utils/webhookEndpoints.js +1 -0
- package/dist/client/utils/webhookEndpoints.js.map +1 -1
- package/dist/client/webhooks/processors.d.ts.map +1 -1
- package/dist/client/webhooks/processors.js +1 -0
- package/dist/client/webhooks/processors.js.map +1 -1
- package/dist/component/_generated/component.d.ts +322 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/costRecovery/claims.d.ts +68 -0
- package/dist/component/costRecovery/claims.d.ts.map +1 -0
- package/dist/component/costRecovery/claims.js +92 -0
- package/dist/component/costRecovery/claims.js.map +1 -0
- package/dist/component/costRecovery/completion.d.ts +39 -0
- package/dist/component/costRecovery/completion.d.ts.map +1 -0
- package/dist/component/costRecovery/completion.js +69 -0
- package/dist/component/costRecovery/completion.js.map +1 -0
- package/dist/component/costRecovery/helpers.d.ts +68 -0
- package/dist/component/costRecovery/helpers.d.ts.map +1 -0
- package/dist/component/costRecovery/helpers.js +71 -0
- package/dist/component/costRecovery/helpers.js.map +1 -0
- package/dist/component/costRecovery/mutations.d.ts +210 -0
- package/dist/component/costRecovery/mutations.d.ts.map +1 -0
- package/dist/component/costRecovery/mutations.js +93 -0
- package/dist/component/costRecovery/mutations.js.map +1 -0
- package/dist/component/costRecovery/queries.d.ts +108 -0
- package/dist/component/costRecovery/queries.d.ts.map +1 -0
- package/dist/component/costRecovery/queries.js +73 -0
- package/dist/component/costRecovery/queries.js.map +1 -0
- package/dist/component/costRecovery/reservations.d.ts +95 -0
- package/dist/component/costRecovery/reservations.d.ts.map +1 -0
- package/dist/component/costRecovery/reservations.js +134 -0
- package/dist/component/costRecovery/reservations.js.map +1 -0
- package/dist/component/costRecovery/schema.d.ts +382 -0
- package/dist/component/costRecovery/schema.d.ts.map +1 -0
- package/dist/component/costRecovery/schema.js +115 -0
- package/dist/component/costRecovery/schema.js.map +1 -0
- package/dist/component/schema.d.ts +135 -0
- package/dist/component/schema.js +5 -0
- package/dist/component/schema.js.map +1 -1
- package/dist/react/components/PriceCard.d.ts +3 -1
- package/dist/react/components/PriceCard.d.ts.map +1 -1
- package/dist/react/components/PriceCard.js +2 -2
- package/dist/react/components/PriceCard.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/price-helpers.d.ts +4 -6
- package/dist/react/lib/price-helpers.d.ts.map +1 -1
- package/dist/react/lib/price-helpers.js +16 -9
- package/dist/react/lib/price-helpers.js.map +1 -1
- package/dist/react/workflows/Collections.d.ts.map +1 -1
- package/dist/react/workflows/Collections.js +2 -1
- package/dist/react/workflows/Collections.js.map +1 -1
- package/dist/react/workflows/PlanPricingEditor.d.ts +4 -0
- package/dist/react/workflows/PlanPricingEditor.d.ts.map +1 -1
- package/dist/react/workflows/PlanPricingEditor.js +12 -9
- package/dist/react/workflows/PlanPricingEditor.js.map +1 -1
- package/dist/react/workflows/Refunds.d.ts +14 -3
- package/dist/react/workflows/Refunds.d.ts.map +1 -1
- package/dist/react/workflows/Refunds.js +58 -16
- package/dist/react/workflows/Refunds.js.map +1 -1
- package/dist/react/workflows/index.d.ts +3 -1
- package/dist/react/workflows/index.d.ts.map +1 -1
- package/dist/react/workflows/index.js +2 -1
- package/dist/react/workflows/index.js.map +1 -1
- package/dist/react/workflows/refund-preview.d.ts +16 -0
- package/dist/react/workflows/refund-preview.d.ts.map +1 -0
- package/dist/react/workflows/refund-preview.js +38 -0
- package/dist/react/workflows/refund-preview.js.map +1 -0
- package/docs/react-workflows.md +167 -1
- package/package.json +2 -2
- package/src/client/billing/balanceFunding.test.ts +257 -0
- package/src/client/billing/balanceFunding.ts +183 -0
- package/src/client/billing/balanceFundingTransport.test.ts +89 -0
- package/src/client/billing/invoicePreview.test.ts +123 -0
- package/src/client/billing/invoicePreview.ts +94 -0
- package/src/client/billing/invoicePreviewTransport.test.ts +70 -0
- package/src/client/charge-currency-format.ts +29 -0
- package/src/client/charge-currency.test.ts +113 -0
- package/src/client/charge-currency.ts +119 -0
- package/src/client/costRecovery-invoices.test.ts +401 -0
- package/src/client/costRecovery-invoices.transport.test.ts +107 -0
- package/src/client/costRecovery-invoices.ts +144 -0
- package/src/client/costRecovery.test.ts +76 -0
- package/src/client/costRecovery.ts +254 -0
- package/src/client/index.ts +76 -75
- package/src/client/products/prices.ts +13 -11
- package/src/client/products/products.ts +10 -10
- package/src/client/settlement/processingCosts.test.ts +349 -0
- package/src/client/settlement/processingCosts.ts +315 -0
- package/src/client/settlement/processingCostsTransport.test.ts +135 -0
- package/src/client/utils/webhookEndpoints.test.ts +5 -0
- package/src/client/utils/webhookEndpoints.ts +1 -0
- package/src/client/webhooks/hooks.test.ts +9 -0
- package/src/client/webhooks/payout-canceled.integration.test.ts +192 -0
- package/src/client/webhooks/processors.ts +1 -0
- package/src/component/_generated/api.ts +12 -0
- package/src/component/_generated/component.ts +407 -0
- package/src/component/cost-recovery.test.ts +344 -0
- package/src/component/costRecovery/claims.ts +119 -0
- package/src/component/costRecovery/completion.ts +92 -0
- package/src/component/costRecovery/helpers.ts +83 -0
- package/src/component/costRecovery/mutations.ts +115 -0
- package/src/component/costRecovery/queries.ts +88 -0
- package/src/component/costRecovery/reservations.ts +164 -0
- package/src/component/costRecovery/schema.ts +135 -0
- package/src/component/schema.ts +10 -0
- package/src/react/components/PayoutSchedule.render.test.tsx +15 -0
- package/src/react/components/PriceCard.render.test.tsx +37 -0
- package/src/react/components/PriceCard.tsx +9 -1
- package/src/react/index.test.ts +5 -0
- package/src/react/index.ts +7 -0
- package/src/react/lib/price-helpers.test.ts +15 -2
- package/src/react/lib/price-helpers.ts +25 -8
- package/src/react/workflows/Billing.test.tsx +1 -1
- package/src/react/workflows/Collections.test.tsx +36 -0
- package/src/react/workflows/Collections.tsx +3 -1
- package/src/react/workflows/PlanPricingEditor.currency.test.tsx +164 -0
- package/src/react/workflows/PlanPricingEditor.tsx +26 -10
- package/src/react/workflows/Refunds.currency.test.tsx +290 -0
- package/src/react/workflows/Refunds.tsx +115 -25
- package/src/react/workflows/index.ts +8 -1
- package/src/react/workflows/refund-preview.test.ts +65 -0
- package/src/react/workflows/refund-preview.ts +56 -0
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,36 @@
|
|
|
14
14
|
- Remove internal tracker references from public documentation and add a
|
|
15
15
|
regression check that prevents their reintroduction.
|
|
16
16
|
|
|
17
|
+
## [0.13.0](https://github.com/FantasticDevHQ/better-stripe/compare/v0.12.0...v0.13.0) (2026-10-15)
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
- **recovery:** reconcile seller cost invoice adjustments ([#159](https://github.com/FantasticDevHQ/better-stripe/issues/159)) ([d1ffdbc](https://github.com/FantasticDevHQ/better-stripe/commit/d1ffdbcb224b6c31ae958c576a4bcd6ff4a0a4dd))
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
- **example:** bind reversal inputs to original charge currency ([#160](https://github.com/FantasticDevHQ/better-stripe/issues/160)) ([6b86926](https://github.com/FantasticDevHQ/better-stripe/commit/6b86926c7d4a173babd2964d4eee086f61c48f63))
|
|
26
|
+
- **example:** reconcile unsettled disputed sources ([#161](https://github.com/FantasticDevHQ/better-stripe/issues/161)) ([9c732bd](https://github.com/FantasticDevHQ/better-stripe/commit/9c732bdc88555210d2172067e6bbf5d2fc513190))
|
|
27
|
+
- **react:** isolate refund preview amounts by currency and status ([#162](https://github.com/FantasticDevHQ/better-stripe/issues/162)) ([601d509](https://github.com/FantasticDevHQ/better-stripe/commit/601d50923dc98f212b29fd4e4e3d856dfffbda44))
|
|
28
|
+
- **react:** preserve original refund currency units ([#157](https://github.com/FantasticDevHQ/better-stripe/issues/157)) ([494827f](https://github.com/FantasticDevHQ/better-stripe/commit/494827f1cb35b0b0ca10b1c6da38a1095bb32500))
|
|
29
|
+
|
|
30
|
+
## [0.12.0](https://github.com/FantasticDevHQ/better-stripe/compare/v0.11.0...v0.12.0) (2026-10-15)
|
|
31
|
+
|
|
32
|
+
### Features
|
|
33
|
+
|
|
34
|
+
- **billing:** add consented balance funding operations ([#149](https://github.com/FantasticDevHQ/better-stripe/issues/149)) ([16434c4](https://github.com/FantasticDevHQ/better-stripe/commit/16434c4eeef343045b4ba66516221232dfa57e06))
|
|
35
|
+
- **billing:** expose complete upcoming invoice previews ([#147](https://github.com/FantasticDevHQ/better-stripe/issues/147)) ([6d81cec](https://github.com/FantasticDevHQ/better-stripe/commit/6d81cec1b458fadbe4aeb69b9611d6ba208d87ce))
|
|
36
|
+
- **recovery:** add durable cost claims and reservations ([#151](https://github.com/FantasticDevHQ/better-stripe/issues/151)) ([a958c3a](https://github.com/FantasticDevHQ/better-stripe/commit/a958c3aa12178de4f22c0c614b0b9e5d7eb37819))
|
|
37
|
+
- **settlement:** expose complete processing cost context ([#150](https://github.com/FantasticDevHQ/better-stripe/issues/150)) ([3933d6b](https://github.com/FantasticDevHQ/better-stripe/commit/3933d6bf3c54181e8f7c27b35e6eb6802fb7c2d8))
|
|
38
|
+
|
|
39
|
+
### Bug Fixes
|
|
40
|
+
|
|
41
|
+
- **react:** preserve charge currency units in price cards ([#155](https://github.com/FantasticDevHQ/better-stripe/issues/155)) ([b0967ae](https://github.com/FantasticDevHQ/better-stripe/commit/b0967ae61ac8ae85adc4f2d8f375e2c71ad1464f))
|
|
42
|
+
- **react:** preserve plan pricing currency units ([#156](https://github.com/FantasticDevHQ/better-stripe/issues/156)) ([f65e712](https://github.com/FantasticDevHQ/better-stripe/commit/f65e712a91dd55e6979ccea540627d4c437dfc6c))
|
|
43
|
+
- **release:** keep generated notes public ([#153](https://github.com/FantasticDevHQ/better-stripe/issues/153)) ([d67f21a](https://github.com/FantasticDevHQ/better-stripe/commit/d67f21a6315c0498db7358b6b37669520037fefa))
|
|
44
|
+
- **release:** preserve historical code examples ([#154](https://github.com/FantasticDevHQ/better-stripe/issues/154)) ([1c3c9f0](https://github.com/FantasticDevHQ/better-stripe/commit/1c3c9f0a63e7c5eb77e685dc0bb3963a4e7dc935))
|
|
45
|
+
- **webhooks:** reconcile dedicated payout cancellations ([#152](https://github.com/FantasticDevHQ/better-stripe/issues/152)) ([9dd84e2](https://github.com/FantasticDevHQ/better-stripe/commit/9dd84e22473a35445a0445a82e8fcdf2eb9ace23))
|
|
46
|
+
|
|
17
47
|
## [0.11.0](https://github.com/FantasticDevHQ/better-stripe/compare/v0.10.2...v0.11.0) (2026-10-14)
|
|
18
48
|
|
|
19
49
|
### Features
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ See the [customer preview setup and webhook migration guide](docs/accounts-v2-pr
|
|
|
38
38
|
|
|
39
39
|
## Status
|
|
40
40
|
|
|
41
|
-
**Version**: `0.
|
|
41
|
+
**Version**: `0.13.0` <!-- x-release-please-version --> (pre-1.0). API may change between minor versions until 1.0.
|
|
42
42
|
|
|
43
43
|
Used in production by its authors. Webhook pipeline is covered by unit tests and a live E2E harness (`npm run e2e:webhooks`) that fires real Stripe-signed events and asserts the ledger.
|
|
44
44
|
|
|
@@ -335,6 +335,257 @@ Methods named `get<Entity>` take the component document ID (exception: `getInvoi
|
|
|
335
335
|
| `reactivateSubscription(ctx, { stripeSubscriptionId })` | Reactivate a canceled subscription |
|
|
336
336
|
| `updateSubscriptionQuantity(ctx, { stripeSubscriptionId, quantity })` | Update subscription seat quantity |
|
|
337
337
|
|
|
338
|
+
### Durable recovery claims
|
|
339
|
+
|
|
340
|
+
`stripe.costRecovery` is a server-only recovery client backed by the Convex
|
|
341
|
+
component. Bookkeeping methods do not use Stripe credentials or move money.
|
|
342
|
+
Invoice methods use the configured Stripe client and must run in an action.
|
|
343
|
+
Your host authenticates the
|
|
344
|
+
caller, approves which costs are owed, authorizes the account and recovery scope,
|
|
345
|
+
and verifies provider outcomes before confirming collection. A recovery scope is
|
|
346
|
+
an application-defined liability boundary, such as one store. Amounts are signed
|
|
347
|
+
integer minor units and currencies never mix.
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
const recovery = stripe.costRecovery;
|
|
351
|
+
const scope = {
|
|
352
|
+
accountId: sellerAccountId,
|
|
353
|
+
scopeKey: storeId,
|
|
354
|
+
currency: "usd",
|
|
355
|
+
};
|
|
356
|
+
await recovery.recordClaim(ctx, {
|
|
357
|
+
...scope,
|
|
358
|
+
claimKey: `cost:${chargeId}`,
|
|
359
|
+
sourceChargeId: chargeId,
|
|
360
|
+
sourceGross: originalGross,
|
|
361
|
+
sourceProcessingFee: originalProviderFee,
|
|
362
|
+
amountDue: approvedCumulativeCost,
|
|
363
|
+
revision: durableObservationRevision,
|
|
364
|
+
});
|
|
365
|
+
const result = await recovery.reserve(ctx, {
|
|
366
|
+
...scope,
|
|
367
|
+
groupKey: `settlement:${fundingChargeId}`,
|
|
368
|
+
operationKey: `settlement:${fundingChargeId}:batch:1`,
|
|
369
|
+
sourceKind: "settlement",
|
|
370
|
+
sourceId: fundingChargeId,
|
|
371
|
+
sourceGross: fundingGross,
|
|
372
|
+
sourceProcessingFee: fundingProviderFee,
|
|
373
|
+
maxDebit: availableSellerEarnings,
|
|
374
|
+
maxCredit: approvedCreditBudget,
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Claims retain their original charge, account, scope, currency, gross and provider
|
|
379
|
+
fee. Replays of the same revision must have the same amount; lower revisions are
|
|
380
|
+
ignored. The host must assign durable, monotonically increasing observation
|
|
381
|
+
revisions from authoritative reconciliation, not an unordered event arrival
|
|
382
|
+
counter. Use `getClaim(ctx, { claimKey })` to capture the prior revision before
|
|
383
|
+
reading provider state, then propose the next revision. On a conflicting revision,
|
|
384
|
+
retry the full provider reconciliation, rather than incrementing the revision of
|
|
385
|
+
an already-stale snapshot. Later provider observations update cumulative `amountDue`, including
|
|
386
|
+
signed credits, without erasing collected or reserved history. Claim keys beginning
|
|
387
|
+
with `rollback:` are reserved by the package.
|
|
388
|
+
|
|
389
|
+
Reservations use maintained totals across all claims, so a credit beyond the
|
|
390
|
+
first history page still reduces the recoverable amount. Each call allocates at
|
|
391
|
+
most 100 claims. Repeat with distinct, durable batch operation keys in the same
|
|
392
|
+
group until `seal(ctx, { groupKey })` succeeds. The group's debit/credit budgets
|
|
393
|
+
apply across all batches. A group identifies exactly one external money boundary;
|
|
394
|
+
only one group per account/scope/currency may remain pending. Retry the same batch
|
|
395
|
+
key to recover the same allocation after a timeout or host write failure.
|
|
396
|
+
|
|
397
|
+
Seal before any provider side effect. New claim observations can require another
|
|
398
|
+
batch before sealing; observations after sealing remain owed or credited for a
|
|
399
|
+
later group. After verifying the provider's final result, call
|
|
400
|
+
`finish(ctx, { groupKey, outcome: "collect" | "release" })`. Completion processes
|
|
401
|
+
at most ten reservations per call, each with at most 100 claims. Repeat while
|
|
402
|
+
`isDone` is false using separate action/job invocations when needed to respect
|
|
403
|
+
Convex transaction limits. Completion is idempotent; a collected group cannot be
|
|
404
|
+
released or reused with another outcome. Do not release an unknown provider
|
|
405
|
+
outcome just because a request timed out. An unpaid invoice must stay reserved
|
|
406
|
+
until its verified payment or cancellation policy resolves it.
|
|
407
|
+
|
|
408
|
+
For a collected settlement group whose funding sale is later returned, call
|
|
409
|
+
`rollback(ctx, { groupKey, returnedPrincipal, revision })` with the authorized
|
|
410
|
+
cumulative principal. This records a separate, source-linked rollback claim,
|
|
411
|
+
preserves the original costs and collection history, and rounds positive and
|
|
412
|
+
negative amounts symmetrically. Reinstated principal can reduce the rollback
|
|
413
|
+
again with a newer revision. This bookkeeping operation does not refund or
|
|
414
|
+
reverse a transfer. For invoice groups, use the verified funding adapter below;
|
|
415
|
+
do not invent invoice payment allocations.
|
|
416
|
+
|
|
417
|
+
Use `balance(ctx, scope)` for exact maintained totals and
|
|
418
|
+
`listClaims(ctx, { ...scope, limit, cursor })` for paginated history. `getClaim`, `getGroup` and
|
|
419
|
+
`getReservation` resume durable workflows. Integrate these server callbacks with
|
|
420
|
+
the existing unstyled React `CostRecovery` display. These bookkeeping APIs leave
|
|
421
|
+
consent, provider fee policy and entitlement changes in the host.
|
|
422
|
+
|
|
423
|
+
### Invoice recovery adjustments
|
|
424
|
+
|
|
425
|
+
After authorizing the seller, invoice and liability scope, reserve with
|
|
426
|
+
`sourceKind: "invoice"` and `sourceId: invoiceId`. Allocate bounded batches and
|
|
427
|
+
seal the group before calling Stripe. Then attach exactly one signed adjustment
|
|
428
|
+
for the entire group:
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
const receipt = await recovery.attachInvoice(ctx, { ...scope, groupKey });
|
|
432
|
+
// Invoke from your invoice lifecycle reconciliation, and repeat bounded completion
|
|
433
|
+
// until isDone after payment or release.
|
|
434
|
+
const result = await recovery.reconcileInvoice(ctx, {
|
|
435
|
+
...scope,
|
|
436
|
+
groupKey,
|
|
437
|
+
releaseUncollectible: false,
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
The invoice must belong to the authorized customer account, use the same currency,
|
|
442
|
+
and be draft before a new item is created. The package stores its stable group
|
|
443
|
+
identity before the provider call, uses that identity as the idempotency key and
|
|
444
|
+
item metadata, and persists the resulting item ID. Every retry scans all items
|
|
445
|
+
on that exact invoice, recovering a successful creation even if the subsequent
|
|
446
|
+
database write failed or the provider idempotency key expired. A bound item that
|
|
447
|
+
has been deleted, changed or duplicated requires reconciliation; the package
|
|
448
|
+
never silently recreates it. Negative adjustments are credits. Items are not
|
|
449
|
+
discountable.
|
|
450
|
+
|
|
451
|
+
Your host must durably serialize provider operations for each group, including
|
|
452
|
+
attachment and any calls that finish or release it. Never release a reservation
|
|
453
|
+
after a timeout or unknown provider outcome. Draft and open invoices retain their
|
|
454
|
+
reservations, blocking recovery from a second payout. A verified paid invoice
|
|
455
|
+
commits collection; a verified void releases the obligation for a future boundary.
|
|
456
|
+
Uncollectible invoices stay reserved unless your explicit host policy passes
|
|
457
|
+
`releaseUncollectible: true`. Do not use that policy while another process might
|
|
458
|
+
retry payment on the original invoice. Subscription cancellation alone does not
|
|
459
|
+
prove its invoices were voided. These methods never attempt card fallback or
|
|
460
|
+
change a payout schedule.
|
|
461
|
+
|
|
462
|
+
For a collected invoice fully funded by one PaymentIntent, call
|
|
463
|
+
`rollbackInvoice(ctx, { ...scope, groupKey, revision })`. It checks the paid
|
|
464
|
+
invoice, completely paginates its payments, verifies the funding charge and its
|
|
465
|
+
refund/dispute transactions, retains the original funding economics, and records
|
|
466
|
+
a separate proportional rollback claim. Use a durable revision captured before
|
|
467
|
+
provider reconciliation, and retry the full read on revision conflicts. Original
|
|
468
|
+
cost claims remain intact. Multiple payments, partial allocations, mixed customer
|
|
469
|
+
credits and non-PaymentIntent funding return
|
|
470
|
+
`{ status: "not_ready", reason: "invoice_funding_allocation_required" }` without
|
|
471
|
+
changing claims. Provider processing costs can also return their documented
|
|
472
|
+
not-ready reasons. Those cases need a host-approved allocation policy rather than
|
|
473
|
+
guessed amounts. Invoice integration in the example is a separate migration.
|
|
474
|
+
|
|
475
|
+
Provider methods preserve the configured API version and propagate Stripe errors.
|
|
476
|
+
See Stripe's [invoice item creation](https://docs.stripe.com/api/invoiceitems/create)
|
|
477
|
+
and [invoice item pagination](https://docs.stripe.com/api/invoiceitems/list).
|
|
478
|
+
|
|
479
|
+
### Refund and dispute processing costs
|
|
480
|
+
|
|
481
|
+
`getProcessingCostContext(ctx, { paymentIntentId, stripeAccountId? })` reads
|
|
482
|
+
provider costs for an authorized payment, including refunded and disputed
|
|
483
|
+
charges. Authorize both the payment and optional connected-account namespace in
|
|
484
|
+
your host. The client preserves its configured API version and provider errors;
|
|
485
|
+
it does not settle a sale, move money, create debt or decide which costs a seller
|
|
486
|
+
owes. The example's cost adapter uses this method while keeping its billing policy
|
|
487
|
+
in the application.
|
|
488
|
+
|
|
489
|
+
A `ready` result contains every refund and dispute, source-linked balance
|
|
490
|
+
transactions, signed fee adjustments and provider amounts in integer minor units.
|
|
491
|
+
Dates are milliseconds. `processingFee` is the original charge balance
|
|
492
|
+
transaction's total provider-reported fee, which can include application fees on
|
|
493
|
+
direct charges. Each transaction exposes `feeDetails` so your approved policy can
|
|
494
|
+
distinguish processor, application and other fees. A null breakdown means provider
|
|
495
|
+
details were unavailable; the reported total remains authoritative. Negative fee
|
|
496
|
+
adjustments remain credits. Failed refunds include their failure reversal instead
|
|
497
|
+
of counting their principal as returned.
|
|
498
|
+
|
|
499
|
+
`principalReturned` caps the combined economic refund/dispute principal at the
|
|
500
|
+
original gross amount. Raw signed provider transactions remain intact, including
|
|
501
|
+
any overlapping debits, and are not a seller debt calculation. A won dispute with
|
|
502
|
+
an unreinstated principal withdrawal is `not_ready`. Pending refunds, missing cost
|
|
503
|
+
transactions or different settlement currencies also return an explicit
|
|
504
|
+
`not_ready` reason instead of estimated or mixed-currency totals. Provider
|
|
505
|
+
pagination errors throw rather than returning partial data. Retry delayed
|
|
506
|
+
provider data; resolve currency conversion through an explicit host policy.
|
|
507
|
+
|
|
508
|
+
See Stripe's [balance transaction fields](https://docs.stripe.com/api/balance_transactions/object)
|
|
509
|
+
and [refund failure reversals](https://docs.stripe.com/api/refunds/object).
|
|
510
|
+
|
|
511
|
+
### Optional balance-funded subscriptions
|
|
512
|
+
|
|
513
|
+
Balance funding requires an eligible connected account and Stripe's Accounts v2
|
|
514
|
+
balance-payment preview. It is separate from non-Connect customer-only Accounts
|
|
515
|
+
v2 eligibility. Keep your normal client on GA; construct a separate client with
|
|
516
|
+
`stripeApiVersion: STRIPE_CUSTOMER_PREVIEW_API_VERSION` for this optional feature.
|
|
517
|
+
`getBalanceFundingCapability()` reports whether that client is explicitly
|
|
518
|
+
configured. It does not claim the account has provider approval; Stripe validates
|
|
519
|
+
eligibility when preparing the payment method.
|
|
520
|
+
|
|
521
|
+
After authenticating the seller, authorizing the subscription, and recording
|
|
522
|
+
explicit recurring-debit consent in your own database:
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
const preparation = await balanceStripe.prepareBalanceFunding(ctx, {
|
|
526
|
+
customerAccount: authorizedSellerAccountId,
|
|
527
|
+
stripeSubscriptionId: authorizedSubscriptionId,
|
|
528
|
+
consent: true,
|
|
529
|
+
idempotencyKey: durablePreparationRequestId,
|
|
530
|
+
});
|
|
531
|
+
// Persist this immutable server-side record BEFORE enabling balance payments.
|
|
532
|
+
await savePreparationAndConsent(preparation);
|
|
533
|
+
await balanceStripe.setBalanceFunding(ctx, {
|
|
534
|
+
preparation,
|
|
535
|
+
enabled: true,
|
|
536
|
+
consent: true,
|
|
537
|
+
idempotencyKey: durableEnableRequestId,
|
|
538
|
+
});
|
|
539
|
+
// Opt-out restores the previous default method and payment-method types.
|
|
540
|
+
await balanceStripe.setBalanceFunding(ctx, {
|
|
541
|
+
preparation: await loadPersistedPreparation(),
|
|
542
|
+
enabled: false,
|
|
543
|
+
idempotencyKey: durableDisableRequestId,
|
|
544
|
+
});
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
`prepareBalanceFunding` creates/attaches a `stripe_balance` method and captures
|
|
548
|
+
the prior subscription settings without changing them. `setBalanceFunding`
|
|
549
|
+
checks subscription ownership and payment-method ownership, then changes the
|
|
550
|
+
settings or recognizes that the requested state already exists. Null prior
|
|
551
|
+
settings restore inheritance from the customer's settings. External source
|
|
552
|
+
changes cause an error instead of being overwritten. Provider errors are
|
|
553
|
+
preserved. Never accept restoration records or account IDs from an unauthenticated
|
|
554
|
+
browser as authority.
|
|
555
|
+
|
|
556
|
+
Serialize changes per subscription in the host. Persist the operation identity
|
|
557
|
+
before the provider call, reuse it after a timeout or host write failure, and use
|
|
558
|
+
a new identity for each later enable/disable cycle. The example's durable pending
|
|
559
|
+
operation prevents an opposite change from racing an unfinished operation; retry
|
|
560
|
+
the pending change first. Preserve the preparation through retries. Keys must be
|
|
561
|
+
nonempty, contain no line breaks, and be at most 247 characters because the package
|
|
562
|
+
adds phase suffixes. Stripe's idempotency retention window still applies.
|
|
563
|
+
|
|
564
|
+
Only eligible processing earnings can fund these subscriptions; top-ups cannot.
|
|
565
|
+
Insufficient available balance in the invoice currency fails payment and does not
|
|
566
|
+
automatically charge a saved card. Handle `invoice.payment_failed` in your host
|
|
567
|
+
and offer an explicit alternate-method recovery flow. These operations neither
|
|
568
|
+
pay an overdue invoice nor change payout schedules. See Stripe's
|
|
569
|
+
[balance-payment eligibility, approval and recovery guidance](https://docs.stripe.com/payments/pay-with-balance).
|
|
570
|
+
|
|
571
|
+
### Upcoming invoice preview
|
|
572
|
+
|
|
573
|
+
Authorize the customer account and subscription in your host action, then call
|
|
574
|
+
`stripe.getUpcomingInvoicePreview(ctx, { customerAccount, stripeSubscriptionId })`.
|
|
575
|
+
The method verifies that the subscription belongs to that account and uses your
|
|
576
|
+
configured Stripe API version. It retrieves every invoice line, returns integer
|
|
577
|
+
minor-unit amounts and millisecond timestamps, and preserves Stripe errors,
|
|
578
|
+
including unavailable previews. Failed pagination throws instead of returning a
|
|
579
|
+
partial estimate. `nextBillingAt` uses Stripe's next payment attempt, trial end,
|
|
580
|
+
or earliest subscription-item renewal date, in that order.
|
|
581
|
+
|
|
582
|
+
Pass the result through an authenticated callback to the React `InvoiceEstimate`
|
|
583
|
+
component. It displays the provider `total` without summing line amounts. Totals
|
|
584
|
+
can include discounts, tax or credits; `amountDue` can differ from `total` because
|
|
585
|
+
of customer balances. Previews are estimates, not payable invoices. Application
|
|
586
|
+
recovery costs and credits remain host-owned policy. See Stripe's
|
|
587
|
+
[invoice preview documentation](https://docs.stripe.com/api/invoices/create_preview).
|
|
588
|
+
|
|
338
589
|
### Invoice, Payment Method, and Payout
|
|
339
590
|
|
|
340
591
|
| Method | Description |
|
|
@@ -344,6 +595,7 @@ Methods named `get<Entity>` take the component document ID (exception: `getInvoi
|
|
|
344
595
|
| `listInvoices(ctx, { stripeAccountId?, userId?, subscriptionId?, status?, limit? })` | List invoices with optional filters |
|
|
345
596
|
| `listInvoicesByUser(ctx, { userId })` | List all invoices for a user |
|
|
346
597
|
| `getInvoiceFromStripe(ctx, { stripeInvoiceId })` | Fetch latest invoice data directly from Stripe |
|
|
598
|
+
| `getUpcomingInvoicePreview(ctx, { customerAccount, stripeSubscriptionId })` | Preview the next invoice with complete lines, provider totals and millisecond dates |
|
|
347
599
|
| `listPaymentMethods(ctx, { stripeCustomerId, type? })` | List saved payment methods |
|
|
348
600
|
| `attachPaymentMethod(ctx, { paymentMethodId, stripeCustomerId })` | Attach a payment method to a customer account |
|
|
349
601
|
| `detachPaymentMethod(ctx, { paymentMethodId })` | Detach a payment method |
|
|
@@ -493,7 +745,8 @@ The component maintains a `webhookEvents` table that tracks every event by its S
|
|
|
493
745
|
- `customer.subscription.created`, `.updated`, `.deleted`, `.trial_will_end`
|
|
494
746
|
- `invoice.created`, `.finalized`, `.paid`, `.payment_failed`
|
|
495
747
|
- `payment_intent.succeeded`, `.payment_failed`, `.canceled`
|
|
496
|
-
- `payout.created`, `.updated`, `.paid`, `.failed`
|
|
748
|
+
- `payout.created`, `.updated`, `.paid`, `.failed`, `.canceled`
|
|
749
|
+
|
|
497
750
|
- `refund.created`, `.updated`, `.failed`
|
|
498
751
|
- `application_fee.refunded` -- keeps `feeCollectedAmount`/`feeRefundedAmount` accurate on the linked payment
|
|
499
752
|
- `charge.dispute.created`, `.updated`, `.closed`, `.funds_withdrawn`, `.funds_reinstated`
|
|
@@ -502,6 +755,18 @@ The component maintains a `webhookEvents` table that tracks every event by its S
|
|
|
502
755
|
|
|
503
756
|
Note: Subscription and invoice events use v1 Billing API event names even when the billing entity is a V2 account referenced via `customer_account`.
|
|
504
757
|
|
|
758
|
+
Dedicated payout cancellations use the same signed V1 endpoint, payout record
|
|
759
|
+
and connected-account association as other payout events. Generic `onEvent`
|
|
760
|
+
and `events["payout.canceled"]` callbacks run after reconciliation. Cancellation
|
|
761
|
+
does not invoke the paid/completed convenience hook. Normal verified replay is
|
|
762
|
+
deduplicated by the existing event ledger.
|
|
763
|
+
|
|
764
|
+
When upgrading from 0.11.0 or earlier, add `payout.canceled` to the existing V1
|
|
765
|
+
endpoint's selected events. New endpoints created with the corrected package's
|
|
766
|
+
defaults include it. Updating the library alone does not edit an existing provider
|
|
767
|
+
endpoint or recover already missed events. Consumers must adopt a published
|
|
768
|
+
version containing this correction; a merged source fix is not a package release.
|
|
769
|
+
|
|
505
770
|
## Trigger API
|
|
506
771
|
|
|
507
772
|
The trigger system follows the BetterAuth pattern: define callbacks at client init time, export the `syncWebhook`/`asyncWebhook` pair via `webhookHandlers()`, and pass their function references to `registerRoutes` via `webhooks`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import { STRIPE_CUSTOMER_PREVIEW_API_VERSION } from "../apiVersion.js";
|
|
3
|
+
export type BalanceFundingCapability = {
|
|
4
|
+
configured: boolean;
|
|
5
|
+
requiredApiVersion: typeof STRIPE_CUSTOMER_PREVIEW_API_VERSION;
|
|
6
|
+
requiresConnectedAccount: true;
|
|
7
|
+
eligibility: "provider_validates_on_prepare";
|
|
8
|
+
};
|
|
9
|
+
export declare function getBalanceFundingCapability(version: string): BalanceFundingCapability;
|
|
10
|
+
export type BalanceFundingScope = {
|
|
11
|
+
customerAccount: string;
|
|
12
|
+
stripeSubscriptionId: string;
|
|
13
|
+
};
|
|
14
|
+
/** Persist server-side before applying; never accept this record from a browser. */
|
|
15
|
+
export type BalanceFundingPreparation = BalanceFundingScope & {
|
|
16
|
+
paymentMethodId: string;
|
|
17
|
+
previousPaymentMethodId: string | null;
|
|
18
|
+
previousPaymentMethodTypes: string[] | null;
|
|
19
|
+
};
|
|
20
|
+
export type PrepareBalanceFundingOptions = BalanceFundingScope & {
|
|
21
|
+
consent: true;
|
|
22
|
+
idempotencyKey: string;
|
|
23
|
+
};
|
|
24
|
+
export type SetBalanceFundingOptions = {
|
|
25
|
+
preparation: BalanceFundingPreparation;
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
consent?: true;
|
|
28
|
+
idempotencyKey: string;
|
|
29
|
+
};
|
|
30
|
+
/** Capture restoration settings, provision and attach; does not enable billing. */
|
|
31
|
+
export declare function prepareBalanceFunding(stripe: Stripe, version: string, opts: PrepareBalanceFundingOptions): Promise<BalanceFundingPreparation>;
|
|
32
|
+
/** Apply a host-persisted preparation. Host serializes changes per subscription. */
|
|
33
|
+
export declare function setBalanceFunding(stripe: Stripe, version: string, opts: SetBalanceFundingOptions): Promise<{
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
//# sourceMappingURL=balanceFunding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balanceFunding.d.ts","sourceRoot":"","sources":["../../../src/client/billing/balanceFunding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,mCAAmC,EAAE,MAAM,kBAAkB,CAAC;AAIvE,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,OAAO,mCAAmC,CAAC;IAC/D,wBAAwB,EAAE,IAAI,CAAC;IAC/B,WAAW,EAAE,+BAA+B,CAAC;CAC9C,CAAC;AACF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd,wBAAwB,CAO1B;AACD,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AACF,oFAAoF;AACpF,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,GAAG;IAC5D,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,0BAA0B,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC7C,CAAC;AACF,MAAM,MAAM,4BAA4B,GAAG,mBAAmB,GAAG;IAC/D,OAAO,EAAE,IAAI,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AACF,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,yBAAyB,CAAC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AA4BF,mFAAmF;AACnF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,yBAAyB,CAAC,CA0CpC;AAQD,oFAAoF;AACpF,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAsD/B"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { STRIPE_CUSTOMER_PREVIEW_API_VERSION } from "../apiVersion.js";
|
|
2
|
+
import { idempotencyOptions } from "../core/idempotency.js";
|
|
3
|
+
import { resolveOwnerAccount } from "../utils/owner.js";
|
|
4
|
+
export function getBalanceFundingCapability(version) {
|
|
5
|
+
return {
|
|
6
|
+
configured: version === STRIPE_CUSTOMER_PREVIEW_API_VERSION,
|
|
7
|
+
requiredApiVersion: STRIPE_CUSTOMER_PREVIEW_API_VERSION,
|
|
8
|
+
requiresConnectedAccount: true,
|
|
9
|
+
eligibility: "provider_validates_on_prepare",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function request(version, key, phase) {
|
|
13
|
+
if (!getBalanceFundingCapability(version).configured)
|
|
14
|
+
throw new Error("Balance funding requires explicit Stripe preview API configuration.");
|
|
15
|
+
if (typeof key !== "string")
|
|
16
|
+
throw new Error("An explicit idempotency key is required for balance funding.");
|
|
17
|
+
idempotencyOptions(key);
|
|
18
|
+
return idempotencyOptions(`${key}:${phase}`);
|
|
19
|
+
}
|
|
20
|
+
async function subscription(stripe, scope) {
|
|
21
|
+
if (!scope.customerAccount.startsWith("acct_") || !scope.stripeSubscriptionId)
|
|
22
|
+
throw new Error("A customer account and subscription are required.");
|
|
23
|
+
const sub = await stripe.subscriptions.retrieve(scope.stripeSubscriptionId);
|
|
24
|
+
if (resolveOwnerAccount(sub) !== scope.customerAccount)
|
|
25
|
+
throw new Error("Subscription does not belong to the customer account.");
|
|
26
|
+
if (!["active", "past_due", "trialing"].includes(sub.status))
|
|
27
|
+
throw new Error("This subscription cannot change payment sources.");
|
|
28
|
+
return sub;
|
|
29
|
+
}
|
|
30
|
+
function methodId(sub) {
|
|
31
|
+
const pm = sub.default_payment_method;
|
|
32
|
+
return typeof pm === "string" ? pm : (pm?.id ?? null);
|
|
33
|
+
}
|
|
34
|
+
/** Capture restoration settings, provision and attach; does not enable billing. */
|
|
35
|
+
export async function prepareBalanceFunding(stripe, version, opts) {
|
|
36
|
+
const createOptions = request(version, opts.idempotencyKey, "create");
|
|
37
|
+
const attachOptions = request(version, opts.idempotencyKey, "attach");
|
|
38
|
+
if (opts.consent !== true)
|
|
39
|
+
throw new Error("Explicit consent is required for balance funding.");
|
|
40
|
+
const sub = await subscription(stripe, opts);
|
|
41
|
+
const previous = methodId(sub);
|
|
42
|
+
const types = sub.payment_settings?.payment_method_types ?? null;
|
|
43
|
+
if (types?.includes("stripe_balance") ||
|
|
44
|
+
(previous &&
|
|
45
|
+
(await stripe.paymentMethods.retrieve(previous)).type ===
|
|
46
|
+
"stripe_balance"))
|
|
47
|
+
throw new Error("Balance funding is already configured; reuse the persisted restoration record.");
|
|
48
|
+
const pm = await stripe.paymentMethods.create({ type: "stripe_balance" }, createOptions);
|
|
49
|
+
if (pm.type !== "stripe_balance")
|
|
50
|
+
throw new Error("Stripe did not return a balance payment method.");
|
|
51
|
+
const current = await stripe.paymentMethods.retrieve(pm.id);
|
|
52
|
+
if (resolveOwnerAccount(current) === null) {
|
|
53
|
+
await stripe.paymentMethods.attach(pm.id, { customer_account: opts.customerAccount }, attachOptions);
|
|
54
|
+
}
|
|
55
|
+
else if (resolveOwnerAccount(current) !== opts.customerAccount) {
|
|
56
|
+
throw new Error("Balance payment method belongs to a different customer account.");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
customerAccount: opts.customerAccount,
|
|
60
|
+
stripeSubscriptionId: opts.stripeSubscriptionId,
|
|
61
|
+
paymentMethodId: pm.id,
|
|
62
|
+
previousPaymentMethodId: previous,
|
|
63
|
+
previousPaymentMethodTypes: types,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function sameTypes(a, b) {
|
|
67
|
+
return a === null || b === null
|
|
68
|
+
? a === b
|
|
69
|
+
: a.length === b.length &&
|
|
70
|
+
[...a].sort().every((type, i) => type === [...b].sort()[i]);
|
|
71
|
+
}
|
|
72
|
+
/** Apply a host-persisted preparation. Host serializes changes per subscription. */
|
|
73
|
+
export async function setBalanceFunding(stripe, version, opts) {
|
|
74
|
+
if (typeof opts.enabled !== "boolean")
|
|
75
|
+
throw new Error("An explicit enabled state is required for balance funding.");
|
|
76
|
+
const requestOptions = request(version, opts.idempotencyKey, opts.enabled ? "enable" : "disable");
|
|
77
|
+
if (opts.enabled && opts.consent !== true)
|
|
78
|
+
throw new Error("Explicit consent is required for balance funding.");
|
|
79
|
+
const prep = opts.preparation;
|
|
80
|
+
const sub = await subscription(stripe, prep);
|
|
81
|
+
const pm = await stripe.paymentMethods.retrieve(prep.paymentMethodId);
|
|
82
|
+
if (pm.type !== "stripe_balance" ||
|
|
83
|
+
resolveOwnerAccount(pm) !== prep.customerAccount)
|
|
84
|
+
throw new Error("Balance payment method does not belong to the customer account.");
|
|
85
|
+
const original = methodId(sub) === prep.previousPaymentMethodId &&
|
|
86
|
+
sameTypes(sub.payment_settings?.payment_method_types ?? null, prep.previousPaymentMethodTypes);
|
|
87
|
+
const balance = methodId(sub) === prep.paymentMethodId &&
|
|
88
|
+
sameTypes(sub.payment_settings?.payment_method_types ?? null, [
|
|
89
|
+
"stripe_balance",
|
|
90
|
+
]);
|
|
91
|
+
if (!original && !balance)
|
|
92
|
+
throw new Error("Subscription payment settings changed; reconcile before applying this restoration record.");
|
|
93
|
+
if ((opts.enabled && balance) || (!opts.enabled && original))
|
|
94
|
+
return { enabled: opts.enabled };
|
|
95
|
+
await stripe.subscriptions.update(sub.id, {
|
|
96
|
+
default_payment_method: opts.enabled
|
|
97
|
+
? prep.paymentMethodId
|
|
98
|
+
: (prep.previousPaymentMethodId ?? ""),
|
|
99
|
+
payment_settings: {
|
|
100
|
+
payment_method_types: opts.enabled
|
|
101
|
+
? ["stripe_balance"]
|
|
102
|
+
: (prep.previousPaymentMethodTypes ?? ""),
|
|
103
|
+
},
|
|
104
|
+
}, requestOptions);
|
|
105
|
+
return { enabled: opts.enabled };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=balanceFunding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balanceFunding.js","sourceRoot":"","sources":["../../../src/client/billing/balanceFunding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
export type UpcomingInvoicePreviewOptions = {
|
|
3
|
+
/** Host must authorize this customer account before calling. */
|
|
4
|
+
customerAccount: string;
|
|
5
|
+
stripeSubscriptionId: string;
|
|
6
|
+
};
|
|
7
|
+
/** Provider amounts are integer minor units; all dates are milliseconds. */
|
|
8
|
+
export type UpcomingInvoicePreview = {
|
|
9
|
+
stripeInvoiceId: string;
|
|
10
|
+
currency: string;
|
|
11
|
+
total: number;
|
|
12
|
+
subtotal: number;
|
|
13
|
+
amountDue: number;
|
|
14
|
+
nextBillingAt: number;
|
|
15
|
+
periodStart: number;
|
|
16
|
+
periodEnd: number;
|
|
17
|
+
linesComplete: true;
|
|
18
|
+
lines: {
|
|
19
|
+
id: string;
|
|
20
|
+
description: string;
|
|
21
|
+
amount: number;
|
|
22
|
+
currency: string;
|
|
23
|
+
periodStart: number;
|
|
24
|
+
periodEnd: number;
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
27
|
+
export declare function getUpcomingInvoicePreview(stripe: Stripe, opts: UpcomingInvoicePreviewOptions): Promise<UpcomingInvoicePreview>;
|
|
28
|
+
//# sourceMappingURL=invoicePreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoicePreview.d.ts","sourceRoot":"","sources":["../../../src/client/billing/invoicePreview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,MAAM,MAAM,6BAA6B,GAAG;IAC1C,gEAAgE;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,sBAAsB,GAAG;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,IAAI,CAAC;IACpB,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,EAAE,CAAC;CACL,CAAC;AAQF,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,sBAAsB,CAAC,CAsDjC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { resolveOwnerAccount } from "../utils/owner.js";
|
|
2
|
+
function integer(value) {
|
|
3
|
+
if (!Number.isSafeInteger(value))
|
|
4
|
+
throw new Error("Invoice preview amounts and dates must be safe integers.");
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export async function getUpcomingInvoicePreview(stripe, opts) {
|
|
8
|
+
if (!opts.customerAccount.startsWith("acct_") || !opts.stripeSubscriptionId)
|
|
9
|
+
throw new Error("A customer account and subscription are required.");
|
|
10
|
+
const subscription = await stripe.subscriptions.retrieve(opts.stripeSubscriptionId);
|
|
11
|
+
if (resolveOwnerAccount(subscription) !== opts.customerAccount)
|
|
12
|
+
throw new Error("Subscription does not belong to the customer account.");
|
|
13
|
+
const preview = await stripe.invoices.createPreview({
|
|
14
|
+
customer_account: opts.customerAccount,
|
|
15
|
+
subscription: opts.stripeSubscriptionId,
|
|
16
|
+
});
|
|
17
|
+
const lines = [];
|
|
18
|
+
const append = (line) => {
|
|
19
|
+
if (line.currency !== preview.currency)
|
|
20
|
+
throw new Error("Invoice preview line currency does not match the invoice.");
|
|
21
|
+
lines.push({
|
|
22
|
+
id: line.id,
|
|
23
|
+
description: line.description ?? "Subscription",
|
|
24
|
+
amount: integer(line.amount),
|
|
25
|
+
currency: line.currency,
|
|
26
|
+
periodStart: integer(line.period.start * 1000),
|
|
27
|
+
periodEnd: integer(line.period.end * 1000),
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
if (preview.lines.has_more) {
|
|
31
|
+
for await (const line of stripe.invoices.listLineItems(preview.id, {
|
|
32
|
+
limit: 100,
|
|
33
|
+
}))
|
|
34
|
+
append(line);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
preview.lines.data.forEach(append);
|
|
38
|
+
}
|
|
39
|
+
const renewalDates = subscription.items.data.map((item) => item.current_period_end);
|
|
40
|
+
const nextBillingAt = preview.next_payment_attempt ??
|
|
41
|
+
(subscription.status === "trialing" ? subscription.trial_end : null) ??
|
|
42
|
+
Math.min(...renewalDates);
|
|
43
|
+
return {
|
|
44
|
+
stripeInvoiceId: preview.id,
|
|
45
|
+
currency: preview.currency,
|
|
46
|
+
total: integer(preview.total),
|
|
47
|
+
subtotal: integer(preview.subtotal),
|
|
48
|
+
amountDue: integer(preview.amount_due),
|
|
49
|
+
nextBillingAt: integer(nextBillingAt * 1000),
|
|
50
|
+
periodStart: integer(preview.period_start * 1000),
|
|
51
|
+
periodEnd: integer(preview.period_end * 1000),
|
|
52
|
+
linesComplete: true,
|
|
53
|
+
lines,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=invoicePreview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoicePreview.js","sourceRoot":"","sources":["../../../src/client/billing/invoicePreview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Internal exact renderer. Callers establish units and amount eligibility. */
|
|
2
|
+
export declare function renderCurrencyUnits(amount: number, currency: string, apiDecimals: 0 | 2, displayDecimals: 0 | 2, locale: string): string;
|
|
3
|
+
//# sourceMappingURL=charge-currency-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charge-currency-format.d.ts","sourceRoot":"","sources":["../../src/client/charge-currency-format.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,CAAC,GAAG,CAAC,EAClB,eAAe,EAAE,CAAC,GAAG,CAAC,EACtB,MAAM,EAAE,MAAM,GACb,MAAM,CAqBR"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Internal exact renderer. Callers establish units and amount eligibility. */
|
|
2
|
+
export function renderCurrencyUnits(amount, currency, apiDecimals, displayDecimals, locale) {
|
|
3
|
+
const scale = 10n ** BigInt(apiDecimals);
|
|
4
|
+
const units = BigInt(Math.abs(amount));
|
|
5
|
+
const whole = units / scale;
|
|
6
|
+
const signedWhole = amount < 0 ? (whole === 0n ? -0 : -whole) : whole;
|
|
7
|
+
const formatter = new Intl.NumberFormat(locale, {
|
|
8
|
+
style: "currency",
|
|
9
|
+
currency: currency.toUpperCase(),
|
|
10
|
+
minimumFractionDigits: displayDecimals,
|
|
11
|
+
maximumFractionDigits: displayDecimals,
|
|
12
|
+
});
|
|
13
|
+
const digits = new Intl.NumberFormat(locale, { useGrouping: false });
|
|
14
|
+
const fraction = (units % scale).toString().padStart(apiDecimals, "0");
|
|
15
|
+
return formatter
|
|
16
|
+
.formatToParts(signedWhole)
|
|
17
|
+
.map((part) => part.type === "fraction"
|
|
18
|
+
? [...fraction].map((digit) => digits.format(Number(digit))).join("")
|
|
19
|
+
: part.value)
|
|
20
|
+
.join("");
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=charge-currency-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charge-currency-format.js","sourceRoot":"","sources":["../../src/client/charge-currency-format.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,UAAU,mBAAmB,CACjC,MAAc,EACd,QAAgB,EAChB,WAAkB,EAClB,eAAsB,EACtB,MAAc;IAEd,MAAM,KAAK,GAAG,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAC5B,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE;QAC9C,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE;QAChC,qBAAqB,EAAE,eAAe;QACtC,qBAAqB,EAAE,eAAe;KACvC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACvE,OAAO,SAAS;SACb,aAAa,CAAC,WAAW,CAAC;SAC1B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,IAAI,CAAC,IAAI,KAAK,UAAU;QACtB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,CAAC,CAAC,IAAI,CAAC,KAAK,CACf;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|