mirror of
https://github.com/anikeen-com/id.git
synced 2026-11-06 23:58:35 +00:00
Document trials without a payment method
trial_payment_method (TrialPaymentMethod::REQUIRED, the default, or ::OPTIONAL) on app subscriptions, subscriptions and checkout sessions: an optional trial can be confirmed without a payment method and is cancelled at its end if there is still none (cancellation_reason and subscription.cancelled data.reason CancellationReason::TRIAL_ENDED_WITHOUT_PAYMENT_METHOD). subscription.trial_will_end carries data.payment_method_missing.
This commit is contained in:
11 files changed
+151
-3
No files matched your search
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Anikeen\Id\Enums;
|
||||
|
||||
/**
|
||||
* `cancellation_reason` of a subscription (and `data.reason` of `subscription.cancelled`): why
|
||||
* Anikeen ID's billing ended it. Null when the user, the app or an admin cancelled it.
|
||||
*/
|
||||
class CancellationReason
|
||||
{
|
||||
/** Its free trial (`trial_payment_method` optional) ended without a payment method: cancelled at the trial's end, nothing billed. */
|
||||
const TRIAL_ENDED_WITHOUT_PAYMENT_METHOD = 'trial_ended_without_payment_method';
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Anikeen\Id\Enums;
|
||||
|
||||
/**
|
||||
* `trial_payment_method` of a subscription with a free trial: whether the user needs a payment
|
||||
* method to confirm it. Null on subscriptions without a trial.
|
||||
*/
|
||||
class TrialPaymentMethod
|
||||
{
|
||||
/** A payment method is needed to confirm the trial (the default). */
|
||||
const REQUIRED = 'required';
|
||||
|
||||
/**
|
||||
* The trial can be confirmed without a payment method (if nothing else has to be paid). If the
|
||||
* user still has none when the trial ends, the subscription is cancelled then instead of billed
|
||||
* (`cancellation_reason` CancellationReason::TRIAL_ENDED_WITHOUT_PAYMENT_METHOD).
|
||||
*/
|
||||
const OPTIONAL = 'optional';
|
||||
}
|
||||
@@ -40,6 +40,7 @@ class AppCheckoutSessions
|
||||
* trial_days?: null|int,
|
||||
* trial_ends_at?: null|string,
|
||||
* trial_once?: bool,
|
||||
* trial_payment_method?: null|string,
|
||||
* discount?: null|array{label: string, percent_off?: float|string, amount_off?: float|string, duration: string, duration_in_periods?: int}
|
||||
* }>,
|
||||
* return_url?: null|string,
|
||||
@@ -51,6 +52,8 @@ class AppCheckoutSessions
|
||||
* repeated idempotency_key returns the first session instead of a new one.
|
||||
* trial_days / trial_ends_at give a subscription a free trial from the user's
|
||||
* confirmation on; trial_once is allowed on one subscription per group.
|
||||
* trial_payment_method `optional`: the session can be confirmed without a
|
||||
* payment method only if all of its subscriptions are such trials.
|
||||
* discount: see AppSubscriptions::create(); the confirmation page shows it.
|
||||
*
|
||||
* @throws ApiException 422 validation (e.g. return_url), 409 "idempotency_key_conflict"
|
||||
|
||||
@@ -28,6 +28,8 @@ use JsonSerializable;
|
||||
* @property ?string $status draft, trial, active, paused, cancelling, expired or cancelled, see status()
|
||||
* @property ?string $cancels_at when a scheduled cancellation takes effect
|
||||
* @property ?string $trial_ends_at
|
||||
* @property ?string $trial_payment_method required or optional with a trial (TrialPaymentMethod), null without
|
||||
* @property ?string $cancellation_reason why the billing cancelled it (CancellationReason), null otherwise
|
||||
* @property ?object $discount {label, percent_off, amount_off, duration, duration_in_periods, periods_remaining}; periods_remaining is null for `forever`, 0 once it ended
|
||||
* @property ?string $starts_at
|
||||
* @property ?string $ends_at
|
||||
|
||||
@@ -52,6 +52,7 @@ class AppSubscriptions
|
||||
* trial_days?: null|int,
|
||||
* trial_ends_at?: null|string,
|
||||
* trial_once?: bool,
|
||||
* trial_payment_method?: null|string,
|
||||
* discount?: null|array{label: string, percent_off?: float|string, amount_off?: float|string, duration: string, duration_in_periods?: int},
|
||||
* idempotency_key?: null|string
|
||||
* } $attributes With an idempotency_key (max. 191 characters), a retried call returns the
|
||||
@@ -65,6 +66,9 @@ class AppSubscriptions
|
||||
* trial_ends_at: a fixed end of the trial (ISO 8601, in the future), not both.
|
||||
* trial_once: refuse the trial if the user already had one with the app in the
|
||||
* same group (422 here, 409 "trial_already_used" on checkout).
|
||||
* trial_payment_method: TrialPaymentMethod::REQUIRED (default) or ::OPTIONAL,
|
||||
* only with a trial: with `optional` the user may confirm it without a payment
|
||||
* method, and without one at the trial's end it is cancelled instead of billed.
|
||||
* discount: e.g. a coupon, as a negative line next to each billed period:
|
||||
* label (shown to the user, max. 100 characters), percent_off (0.01–100) or
|
||||
* amount_off (net per period, at most the price), duration `once` (the first
|
||||
|
||||
@@ -27,6 +27,8 @@ use Throwable;
|
||||
* @property string $ends_at
|
||||
* @property ?string $cancels_at when a scheduled cancellation takes effect
|
||||
* @property ?string $trial_ends_at
|
||||
* @property ?string $trial_payment_method required or optional with a trial (TrialPaymentMethod), null without
|
||||
* @property ?string $cancellation_reason why the billing cancelled it (CancellationReason), null otherwise
|
||||
* @property ?object $discount {label, percent_off, amount_off, duration, duration_in_periods, periods_remaining}; periods_remaining is null for `forever`, 0 once it ended
|
||||
* @property null|string $confirmation_url where the user confirms it (third-party apps)
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@ class Subscriptions extends BaseCollection
|
||||
* trial_days?: null|int,
|
||||
* trial_ends_at?: null|string,
|
||||
* trial_once?: bool,
|
||||
* trial_payment_method?: null|string,
|
||||
* discount?: null|array{label: string, percent_off?: float|string, amount_off?: float|string, duration: string, duration_in_periods?: int}
|
||||
* } $attributes The subscription data:
|
||||
* - group: The group (optional)
|
||||
@@ -43,6 +44,7 @@ class Subscriptions extends BaseCollection
|
||||
* - trial_days: A free trial of 1–365 days from the user's confirmation on (optional)
|
||||
* - trial_ends_at: Or a fixed end of the free trial (ISO 8601, in the future; optional)
|
||||
* - trial_once: Refuse the trial if the user already had one with the app in the group (optional)
|
||||
* - trial_payment_method: TrialPaymentMethod::REQUIRED (default) or ::OPTIONAL, only with a trial (optional)
|
||||
* - discount: A discount per billed period, see AppSubscriptions::create() (optional)
|
||||
* @throws Throwable
|
||||
*/
|
||||
|
||||
@@ -18,10 +18,15 @@ final class EventType
|
||||
public const SUBSCRIPTION_ACTIVATED = 'subscription.activated';
|
||||
public const SUBSCRIPTION_PAUSED = 'subscription.paused';
|
||||
public const SUBSCRIPTION_RESUMED = 'subscription.resumed';
|
||||
/** `data.reason`: why the billing cancelled it (CancellationReason, e.g. a trial that ended without a payment method), else null. */
|
||||
public const SUBSCRIPTION_CANCELLED = 'subscription.cancelled';
|
||||
public const SUBSCRIPTION_EXPIRED = 'subscription.expired';
|
||||
|
||||
/** The free trial of a running subscription ends in a few days (see trial_ends_at); billing starts then. */
|
||||
/**
|
||||
* The free trial of a running subscription ends in a few days (see trial_ends_at); billing
|
||||
* starts then. `data.payment_method_missing`: the user has no payment method yet (with
|
||||
* trial_payment_method `optional` the subscription then ends with the trial).
|
||||
*/
|
||||
public const SUBSCRIPTION_TRIAL_WILL_END = 'subscription.trial_will_end';
|
||||
|
||||
/** The last discounted period of a subscription was billed; the following ones are billed at the full price. */
|
||||
|
||||
Reference in new issue
Block a user