LatticeStripe. Entitlements. ActiveEntitlementSummary
(LatticeStripe v2.0.0)
Copy Markdown
View Source
A point-in-time snapshot of everything a customer is currently entitled to.
Wire object entitlements.active_entitlement_summary. This object is delivered by
webhook only — it arrives as the data.object of an
entitlements.active_entitlement_summary.updated event, and Stripe serves it from no
HTTP endpoint at all. There is therefore no retrieve/2 here, and that is not a gap:
there is nothing to retrieve it from. The only way to obtain one is to receive it.
It also has no top-level id — the object carries no id property, not even an
optional one, and no x-resourceId, which is an independent structural confirmation that
it is not an addressable resource. The struct consequently has no :id field.
See Entitlements for the end-to-end story.
Stripe inlines at most ten entitlements
The nested entitlements envelope is a page, not a snapshot. Stripe inlines at most
ten active entitlements and sets has_more when there are more. Treating that inline
page as a complete picture is the bug this module was written to remove: a customer with
eleven active entitlements looks unentitled to the eleventh, and a reconciler that writes
the inline page to a local store revokes access the customer legitimately holds.
Use stream_entitlements!/3. It performs a full canonical re-fetch keyed on the
summary's customer, at limit=100 per page, following has_more to the end. It ignores
the inline page entirely and deliberately does not resume from its cursor — resuming would
stitch a head-of-list captured when the webhook fired to a tail queried later, producing a
hybrid whose ordering assumption spans two points in time. One call, one point in time.
The inlined url is rewritten
Stripe's webhook payload sets the inlined list's url to
"/v1/customer/cus_ABC123customer/entitlements" — singular customer, path-scoped. That
string is not one of the paths in Stripe's OpenAPI spec; its callability is not
established and it 404s against stripe-mock. Because LatticeStripe.List.stream/2
builds each next page from list.url, this module rewrites the nested list's url to the
canonical /v1/entitlements/active_entitlements, populates _params with the customer
filter, and derives _last_id from the raw wire maps. A consumer who reaches for
LatticeStripe.List.stream(summary.entitlements, client) instead of the blessed path
therefore still gets a documented, callable, tenant-scoped request.
Reconciling from a webhook
alias LatticeStripe.Entitlements.ActiveEntitlementSummary
def reconcile_from_event(%LatticeStripe.Event{} = event, client) do
summary = ActiveEntitlementSummary.from_map(event.data["object"])
entitlements =
client
|> ActiveEntitlementSummary.stream_entitlements!(summary)
|> Enum.to_list()
MyApp.Entitlements.reconcile(summary.customer, entitlements)
endDispatch it from your LatticeStripe.Webhook.Handler on the
entitlements.active_entitlement_summary.updated event type.
Gate your application against the local store that reconcile/2 writes — never against a
network call on the authorization path. See
LatticeStripe.Entitlements.ActiveEntitlement for why.
Summary
Functions
Decode a Stripe-shaped string-keyed map into an %ActiveEntitlementSummary{}.
Returns a lazy stream of all of the summarized customer's active entitlements.
Types
@type t() :: %LatticeStripe.Entitlements.ActiveEntitlementSummary{ customer: String.t() | nil, entitlements: LatticeStripe.List.t() | nil, extra: map(), livemode: boolean() | nil, object: String.t() | nil }
Functions
Decode a Stripe-shaped string-keyed map into an %ActiveEntitlementSummary{}.
The nested entitlements envelope becomes a LatticeStripe.List whose data is a list
of LatticeStripe.Entitlements.ActiveEntitlement structs, with its url and _params
rewritten for canonical pagination.
Idempotent: applied to an already-decoded struct it returns it unchanged, and
from_map(nil) returns nil. Unknown top-level keys land in :extra.
@spec stream_entitlements!(LatticeStripe.Client.t(), t(), keyword()) :: Enumerable.t()
Returns a lazy stream of all of the summarized customer's active entitlements.
A full canonical re-fetch against /v1/entitlements/active_entitlements at limit=100,
keyed on summary.customer. The summary's inline page is ignored entirely — see the
module documentation for why a cursor-resume would be worse rather than cheaper.
Raises LatticeStripe.Error if any page fetch fails, so a partial enumeration surfaces
as an error rather than as a short list.
entitlements =
client
|> LatticeStripe.Entitlements.ActiveEntitlementSummary.stream_entitlements!(summary)
|> Enum.to_list()There is no non-bang twin, for the same reason
LatticeStripe.Entitlements.ActiveEntitlement.stream!/3 has none.