Gamend.Payments (gamend_core v1.0.1296)

Copy Markdown View Source

Payment catalog, purchase ledger, and entitlements.

Provider-specific integrations validate or create transactions, but this context remains the source of truth for what a user owns inside the game.

Summary

Functions

Counts list_user_entitlements/2's entitlements; takes :include_inactive.

Whether the user has EVER held key, active or not. What a once-per-account grant (a trial) checks, since the row outlives its end.

The user's key row, active or not, or nil.

Grant an entitlement without a purchase: a trial, a contributor's reward, a support gesture. Upserts the one (user, key) row.

Whether the user holds key right now: an active row with no end, or an end still ahead.

Active catalog entries, optionally for one provider. Pass :page and :page_size for one page; without them, every entry.

The user's entitlements, by key: active ones only unless include_inactive: true. Pass :page and :page_size for one page.

Stamp a provider event as fully handled. Only then does a retry of the same event id count as a duplicate.

Functions

admin_stats()

See Gamend.Payments.Admin.admin_stats/0.

cancel_stripe_subscription_at_period_end(user, entitlement_id)

See Gamend.Payments.StripeEvents.cancel_stripe_subscription_at_period_end/2.

count_catalog(provider \\ nil)

@spec count_catalog(String.t() | nil) :: non_neg_integer()

Counts list_catalog/2's entries.

count_entitlements(opts \\ [])

See Gamend.Payments.Admin.count_entitlements/1.

count_products(opts \\ [])

See Gamend.Payments.Admin.count_products/1.

count_provider_events(opts \\ [])

See Gamend.Payments.Admin.count_provider_events/1.

count_provider_products(opts \\ [])

See Gamend.Payments.Admin.count_provider_products/1.

count_purchases(opts \\ [])

See Gamend.Payments.Admin.count_purchases/1.

count_reconciliation_cursors(opts \\ [])

See Gamend.Payments.Admin.count_reconciliation_cursors/1.

count_user_entitlements(user_id, opts \\ [])

@spec count_user_entitlements(Ecto.UUID.t(), keyword()) :: non_neg_integer()

Counts list_user_entitlements/2's entitlements; takes :include_inactive.

create_product(attrs)

@spec create_product(map()) ::
  {:ok, Gamend.Payments.Product.t()} | {:error, Ecto.Changeset.t()}

create_provider_product(attrs)

@spec create_provider_product(map()) ::
  {:ok, Gamend.Payments.ProviderProduct.t()} | {:error, Ecto.Changeset.t()}

create_purchase(user, provider_product, attrs \\ %{})

create_steam_checkout(user, attrs)

@spec create_steam_checkout(Gamend.Accounts.User.t(), map()) ::
  {:ok,
   %{
     purchase: Gamend.Payments.Purchase.t(),
     provider_transaction_id: String.t() | nil,
     steam_url: String.t() | nil
   }}
  | {:error, term()}

create_stripe_billing_portal(user, return_url)

See Gamend.Payments.StripeEvents.create_stripe_billing_portal/2.

create_stripe_checkout(user, attrs)

See Gamend.Payments.StripeEvents.create_stripe_checkout/2.

entitlement_ever?(user_id, key)

@spec entitlement_ever?(Ecto.UUID.t(), String.t()) :: boolean()

Whether the user has EVER held key, active or not. What a once-per-account grant (a trial) checks, since the row outlives its end.

finalize_steam_purchase(user, attrs)

@spec finalize_steam_purchase(Gamend.Accounts.User.t(), map()) ::
  {:ok, %{purchase: Gamend.Payments.Purchase.t()}} | {:error, term()}

fulfill_purchase(purchase, provider_payload \\ %{})

@spec fulfill_purchase(Gamend.Payments.Purchase.t(), map()) ::
  {:ok, Gamend.Payments.Purchase.t()} | {:error, term()}

get_product(id)

@spec get_product(Ecto.UUID.t()) :: Gamend.Payments.Product.t() | nil

get_product_by_sku(sku)

@spec get_product_by_sku(String.t()) :: Gamend.Payments.Product.t() | nil

get_provider_product(id)

@spec get_provider_product(Ecto.UUID.t()) :: Gamend.Payments.ProviderProduct.t() | nil

get_provider_product(provider, external_id)

@spec get_provider_product(String.t(), String.t()) ::
  Gamend.Payments.ProviderProduct.t() | nil

get_purchase(id)

@spec get_purchase(Ecto.UUID.t()) :: Gamend.Payments.Purchase.t() | nil

get_purchase_by_order_id(order_id)

@spec get_purchase_by_order_id(String.t()) :: Gamend.Payments.Purchase.t() | nil

get_purchase_by_provider_original_transaction(provider, transaction_id)

@spec get_purchase_by_provider_original_transaction(String.t(), String.t()) ::
  Gamend.Payments.Purchase.t() | nil

get_purchase_by_provider_transaction(provider, transaction_id)

@spec get_purchase_by_provider_transaction(String.t(), String.t()) ::
  Gamend.Payments.Purchase.t() | nil

get_user_entitlement_by_key(user_id, key)

@spec get_user_entitlement_by_key(Ecto.UUID.t(), String.t()) ::
  Gamend.Payments.Entitlement.t() | nil

The user's key row, active or not, or nil.

grant_entitlement(user_id, key, opts \\ [])

@spec grant_entitlement(Ecto.UUID.t(), String.t(), keyword()) ::
  {:ok, Gamend.Payments.Entitlement.t()} | {:error, term()}

Grant an entitlement without a purchase: a trial, a contributor's reward, a support gesture. Upserts the one (user, key) row.

Never shortens what the user already has: an active row with no end (a lifetime purchase) keeps no end, and an active row ending later than :expires_at keeps its later end. A row a purchase created keeps its source_purchase_id, so its provider sync still finds it.

Options: :expires_at (a DateTime, nil for no end), :metadata (a map merged into the row's, e.g. %{"source" => "trial", "granted_by" => id}).

handle_apple_webhook(raw_body)

See Gamend.Payments.StoreEvents.handle_apple_webhook/1.

handle_google_webhook(raw_body, authorization_header)

See Gamend.Payments.StoreEvents.handle_google_webhook/2.

handle_stripe_webhook(raw_body, signature)

See Gamend.Payments.StripeEvents.handle_stripe_webhook/2.

has_entitlement?(user_id, key)

@spec has_entitlement?(Ecto.UUID.t(), String.t()) :: boolean()

Whether the user holds key right now: an active row with no end, or an end still ahead.

Answered from entitlement_rows/1, so a page asking about several keys (a paid plan and its trial, on every render) costs one query between changes.

list_admin_entitlements(opts \\ [])

See Gamend.Payments.Admin.list_admin_entitlements/1.

list_admin_products(opts \\ [])

See Gamend.Payments.Admin.list_admin_products/1.

list_admin_provider_products(opts \\ [])

See Gamend.Payments.Admin.list_admin_provider_products/1.

list_admin_purchases(opts \\ [])

See Gamend.Payments.Admin.list_admin_purchases/1.

list_catalog(provider \\ nil, opts \\ [])

@spec list_catalog(String.t() | nil, keyword()) :: [
  Gamend.Payments.ProviderProduct.t()
]

Active catalog entries, optionally for one provider. Pass :page and :page_size for one page; without them, every entry.

list_products(opts \\ [])

@spec list_products(keyword()) :: [Gamend.Payments.Product.t()]

list_provider_events(opts \\ [])

See Gamend.Payments.Admin.list_provider_events/1.

list_reconciliation_cursors(opts \\ [])

See Gamend.Payments.Admin.list_reconciliation_cursors/1.

list_user_entitlements(user_id, opts \\ [])

@spec list_user_entitlements(Ecto.UUID.t(), keyword()) :: [
  Gamend.Payments.Entitlement.t()
]

The user's entitlements, by key: active ones only unless include_inactive: true. Pass :page and :page_size for one page.

list_user_purchases(user_id, opts \\ [])

@spec list_user_purchases(Ecto.UUID.t(), keyword()) :: [Gamend.Payments.Purchase.t()]

mark_event_processed(event)

@spec mark_event_processed(Gamend.Payments.ProviderEvent.t()) ::
  {:ok, Gamend.Payments.ProviderEvent.t()} | {:error, Ecto.Changeset.t()}

Stamp a provider event as fully handled. Only then does a retry of the same event id count as a duplicate.

product_entitlement_key(product)

@spec product_entitlement_key(Gamend.Payments.Product.t()) :: String.t()

provider_adapter_statuses()

See Gamend.Payments.Admin.provider_adapter_statuses/0.

reconcile_stripe_purchase(purchase)

See Gamend.Payments.StripeEvents.reconcile_stripe_purchase/1.

record_provider_event(provider, event_id, event_type, payload, metadata \\ %{})

@spec record_provider_event(String.t(), String.t(), String.t(), map(), map()) ::
  {:ok, Gamend.Payments.ProviderEvent.t(), boolean()}
  | {:error, Ecto.Changeset.t()}

revoke_purchase(purchase, attrs \\ %{})

@spec revoke_purchase(Gamend.Payments.Purchase.t(), map()) ::
  {:ok, Gamend.Payments.Purchase.t()} | {:error, term()}

stripe_config_status()

See Gamend.Payments.Admin.stripe_config_status/0.

stripe_customer_id(user)

See Gamend.Payments.StripeEvents.stripe_customer_id/1.

update_product(product, attrs)

@spec update_product(Gamend.Payments.Product.t(), map()) ::
  {:ok, Gamend.Payments.Product.t()} | {:error, Ecto.Changeset.t()}

update_provider_product(provider_product, attrs)

@spec update_provider_product(Gamend.Payments.ProviderProduct.t(), map()) ::
  {:ok, Gamend.Payments.ProviderProduct.t()} | {:error, Ecto.Changeset.t()}

validate_store_purchase(user, provider, attrs)

@spec validate_store_purchase(Gamend.Accounts.User.t(), String.t(), map()) ::
  {:ok, %{purchase: Gamend.Payments.Purchase.t(), seen_before: boolean()}}
  | {:error, term()}