@automattic/shopping-cart
Version:
A library to use the WordPress.com shopping cart.
948 lines (818 loc) • 29 kB
text/typescript
import type { CartActionError } from './errors';
import type { Dispatch } from 'react';
export type ShoppingCartReducerDispatch = ( action: ShoppingCartAction ) => void;
export type ShoppingCartReducer = (
state: ShoppingCartState,
action: ShoppingCartAction
) => ShoppingCartState;
export type CartKey = number | 'no-user' | 'no-site';
export type GetCart = ( cartKey: CartKey ) => Promise< ResponseCart >;
export type SetCart = ( cartKey: CartKey, requestCart: RequestCart ) => Promise< ResponseCart >;
export interface ShoppingCartManagerOptions {
refetchOnWindowFocus?: boolean;
defaultCartKey?: CartKey;
}
export type GetManagerForKey = ( cartKey: CartKey | undefined ) => ShoppingCartManager;
export type GetCartKeyForSiteSlug = ( siteSlug: string ) => Promise< CartKey >;
export interface ShoppingCartManagerClient {
forCartKey: GetManagerForKey;
getCartKeyForSiteSlug: GetCartKeyForSiteSlug;
}
export type UnsubscribeFunction = () => void;
export type SubscribeCallback = () => void;
export type ShoppingCartManagerSubscribe = ( callback: SubscribeCallback ) => UnsubscribeFunction;
export interface SubscriptionManager {
subscribe: ShoppingCartManagerSubscribe;
notifySubscribers: () => void;
}
export interface ShoppingCartManagerState {
isLoading: boolean;
loadingError: string | null | undefined;
loadingErrorType: ShoppingCartError | undefined;
isPendingUpdate: boolean;
responseCart: ResponseCart;
couponStatus: CouponStatus;
}
type WaitForReady = () => Promise< ResponseCart >;
export type ShoppingCartManagerGetState = () => ShoppingCartManagerState;
export interface ShoppingCartManager {
getState: ShoppingCartManagerGetState;
subscribe: ShoppingCartManagerSubscribe;
actions: ShoppingCartManagerActions;
fetchInitialCart: WaitForReady;
}
export type UseShoppingCart = ShoppingCartManagerActions & ShoppingCartManagerState;
export type ReplaceProductInCart = (
uuidToReplace: string,
productPropertiesToChange: Partial< RequestCartProduct >
) => Promise< ResponseCart >;
export type ReloadCartFromServer = () => Promise< ResponseCart >;
export type ClearCartMessages = () => Promise< ResponseCart >;
export type ReplaceProductsInCart = (
products: MinimalRequestCartProduct[]
) => Promise< ResponseCart >;
export type AddProductsToCart = (
products: MinimalRequestCartProduct[]
) => Promise< ResponseCart >;
export type RemoveCouponFromCart = () => Promise< ResponseCart >;
export type ApplyCouponToCart = ( couponId: string ) => Promise< ResponseCart >;
export type RemoveProductFromCart = ( uuidToRemove: string ) => Promise< ResponseCart >;
export type UpdateTaxLocationInCart = ( location: CartLocation ) => Promise< ResponseCart >;
export type SetCouponFieldVisible = ( couponFieldVisible: boolean ) => void;
export type RemoveCouponAndClearField = () => Promise< ResponseCart< ResponseCartProduct > >;
/**
* The custom hook keeps a cached version of the server cart, as well as a
* cache status.
*
* - 'fresh': Page has loaded and no requests have been sent.
* - 'fresh-pending': Page has loaded and we are waiting for the initial request.
* - 'invalid': Local cart data has been edited.
* - 'valid': Local cart has been reloaded from the server.
* - 'pending': Request has been sent, awaiting response.
* - 'error': Something went wrong.
*/
export type CacheStatus = 'fresh' | 'fresh-pending' | 'valid' | 'invalid' | 'pending' | 'error';
/**
* Possible states re. coupon submission.
*
* - 'fresh': User has not (yet) attempted to apply a coupon.
* - 'pending': Coupon request has been sent, awaiting response.
* - 'applied': Coupon has been applied to the cart.
* - 'rejected': Coupon code did not apply. The reason should be in the cart errors.
*/
export type CouponStatus = 'fresh' | 'pending' | 'applied' | 'rejected';
export type ShoppingCartAction =
| { type: 'CLEAR_QUEUED_ACTIONS' }
| { type: 'CLEAR_MESSAGES' }
| { type: 'UPDATE_LAST_VALID_CART' }
| { type: 'REMOVE_CART_ITEM'; uuidToRemove: string }
| { type: 'CART_PRODUCTS_ADD'; products: RequestCartProduct[] }
| { type: 'CART_PRODUCTS_REPLACE_ALL'; products: RequestCartProduct[] }
| { type: 'SET_LOCATION'; location: CartLocation }
| {
type: 'CART_PRODUCT_REPLACE';
uuidToReplace: string;
productPropertiesToChange: Partial< RequestCartProduct >;
}
| { type: 'ADD_COUPON'; couponToAdd: string }
| { type: 'REMOVE_COUPON' }
| { type: 'CART_RELOAD' }
| { type: 'RECEIVE_INITIAL_RESPONSE_CART'; initialResponseCart: ResponseCart }
| { type: 'FETCH_INITIAL_RESPONSE_CART' }
| { type: 'REQUEST_UPDATED_RESPONSE_CART' }
| { type: 'RECEIVE_UPDATED_RESPONSE_CART'; updatedResponseCart: ResponseCart }
| { type: 'RAISE_ERROR'; error: ShoppingCartError; message: string };
export interface ShoppingCartManagerActions {
addProductsToCart: AddProductsToCart;
removeProductFromCart: RemoveProductFromCart;
applyCoupon: ApplyCouponToCart;
removeCoupon: RemoveCouponFromCart;
updateLocation: UpdateTaxLocationInCart;
replaceProductInCart: ReplaceProductInCart;
replaceProductsInCart: ReplaceProductsInCart;
reloadFromServer: ReloadCartFromServer;
clearMessages: ClearCartMessages;
}
export type ShoppingCartError = 'GET_SERVER_CART_ERROR' | 'SET_SERVER_CART_ERROR';
export type ShoppingCartState = {
responseCart: TempResponseCart;
lastValidResponseCart: ResponseCart;
couponStatus: CouponStatus;
queuedActions: ShoppingCartAction[];
} & (
| {
cacheStatus: Exclude< CacheStatus, 'error' >;
loadingError?: undefined;
loadingErrorType?: undefined;
}
| {
cacheStatus: 'error';
loadingError: string;
loadingErrorType: ShoppingCartError;
}
);
export interface WithShoppingCartProps {
shoppingCartManager: UseShoppingCart;
cart: ResponseCart;
}
export type CartValidCallback = ( cart: ResponseCart ) => void;
export type DispatchAndWaitForValid = ( action: ShoppingCartAction ) => Promise< ResponseCart >;
export type SavedActionPromise = {
resolve: ( responseCart: ResponseCart ) => void;
reject: ( error: CartActionError ) => void;
};
export interface ActionPromises {
resolve: ( tempResponseCart: TempResponseCart ) => void;
reject: ( error: CartActionError ) => void;
add: ( actionPromise: SavedActionPromise ) => void;
}
export interface CartSyncManager {
syncPendingCartToServer: (
state: ShoppingCartState,
dispatch: Dispatch< ShoppingCartAction >
) => void;
fetchInitialCartFromServer: ( dispatch: Dispatch< ShoppingCartAction > ) => void;
}
export interface RequestCart {
blog_id: number;
cart_key?: CartKey;
products: RequestCartProduct[];
tax: RequestCartTaxData;
coupon: string;
temporary: false;
}
export type RequestCartTaxData = null | {
location: {
country_code: string | undefined;
postal_code: string | undefined;
subdivision_code: string | undefined;
vat_id?: string;
organization?: string;
address?: string;
city?: string;
};
};
export interface RequestCartProduct {
product_slug: string;
product_id?: number;
meta: string;
volume: number;
quantity: number | null;
extra: RequestCartProductExtra;
}
export type MinimalRequestCartProduct = Partial< RequestCartProduct > &
Pick< RequestCartProduct, 'product_slug' >;
export interface ResponseCart< P = ResponseCartProduct > {
blog_id: number;
cart_key: CartKey;
products: P[];
/**
* The amount of tax collected.
* @deprecated This is a float and is unreliable. Use total_tax_integer.
*/
total_tax: string;
/**
* The amount of tax collected in the currency's smallest unit.
*/
total_tax_integer: number;
/**
* The amount of tax collected per product.
*/
total_tax_breakdown: TaxBreakdownItem[];
/**
* The cart's total cost.
* @deprecated This is a float and is unreliable. Use total_cost_integer.
*/
total_cost: number;
/**
* The cart's total cost in the currency's smallest unit.
*/
total_cost_integer: number;
/**
* The difference between the cost before any coupon and the actual price
* for all products in the currency's smallest unit.
*
* Note that the difference may be caused by many factors, not just coupons.
* It's best not to rely on it.
*/
coupon_savings_total_integer: number;
/**
* The subtotal with taxes included in the currency's smallest unit.
*
* This is the sum of each item's price with all discounts (including
* coupons), but without taxes. This does not include credits!
*/
sub_total_with_taxes_integer: number;
/**
* The subtotal without taxes included in the currency's smallest unit.
*
* This is the sum of each item's price with all discounts (including
* coupons), but without taxes. This does not include credits!
*/
sub_total_integer: number;
/**
* The number of credits available in the currency's smallest unit.
*/
credits_integer: number;
/**
* Gift Details
*/
gift_details?: ResponseCartGiftDetails;
/**
* True if the cart contains a purchase for a different user's site.
*/
is_gift_purchase?: boolean;
currency: string;
allowed_payment_methods: string[];
coupon: string;
is_coupon_applied: boolean;
has_auto_renew_coupon_been_automatically_applied: boolean;
locale: string;
is_signup: boolean;
messages?: ResponseCartMessages;
cart_generated_at_timestamp: number;
tax: ResponseCartTaxData;
next_domain_is_free: boolean;
next_domain_condition: '' | 'blog';
bundled_domain?: string;
terms_of_service?: TermsOfServiceRecord[];
has_pending_payment?: boolean;
}
export interface ResponseCartTaxData {
location: {
country_code?: string;
postal_code?: string;
subdivision_code?: string;
vat_id?: string;
organization?: string;
address?: string;
city?: string;
};
display_taxes: boolean;
}
export interface TaxBreakdownItem {
tax_collected: number;
tax_collected_integer: number;
label?: string;
rate: number;
rate_display: string;
}
/**
* Local schema for response cart that can contain incomplete products. This
* schema is only used inside the reducer and will only differ from a
* ResponseCart if the cacheStatus is invalid.
*/
export type TempResponseCart = ResponseCart< RequestCartProduct >;
export interface ResponseCartMessages {
errors?: ResponseCartMessage[];
success?: ResponseCartMessage[];
persistent_errors?: ResponseCartMessage[];
}
export interface ResponseCartMessage {
code: string;
message: string;
}
export interface ResponseCartProduct {
uuid: string;
product_name: string;
product_slug: string;
product_id: number;
currency: string;
product_name_en: string;
/**
* The cart item's original price without volume in the currency's smallest unit.
*
* Discounts and volume are not included, but quantity is included.
*/
item_original_cost_integer: number;
/**
* The monthly term original price of a cart item in the currency's smallest unit.
*/
item_original_monthly_cost_integer: number;
/**
* The cart item's original price with volume in the currency's smallest unit.
*
* Discounts are not included, but volume and quantity are included.
*/
item_original_subtotal_integer: number;
/**
* The cart item's original price for quantity 1 in the currency's smallest unit.
*
* Discounts are not included, but volume is included.
*/
item_original_cost_for_quantity_one_integer: number;
/**
* The cart item's subtotal in the currency's smallest unit.
*
* This is the cost of the item with all discounts (including coupons),
* but without taxes.
*/
item_subtotal_integer: number;
/**
* The cart item's subtotal without volume.
* @deprecated This is a float and is unreliable. Use item_subtotal_integer
*/
cost: number;
/**
* The amount of the local currency deducted by an applied coupon, if any.
* This is in the currency's smallest unit.
*/
coupon_savings_integer?: number;
price_tier_minimum_units?: number | null;
price_tier_maximum_units?: number | null;
/**
* A cost override is a change to the price of a product. The new price and the old (original) price are both provided.
*
* The override_code is a string that identifies the reason for the override.
* When displaying the reason to the customer, use the human_readable_reason.
*/
cost_overrides: ResponseCartCostOverride[];
/**
* If set, is used to transform the usage/quantity of units used to derive the number of units
* we want to bill the customer for, before applying the per unit cost.
*
* To put simply, the purpose of this attribute is to bill the customer at a different granularity compared to their usage.
*/
price_tier_transform_quantity_divide_by?: number | null;
/**
* Used for rounding the number of units we want to bill the customer for (which is derived after dividing the
* usage/quantity of units by the `price_tier_transform_quantity_divide_by` number).
*
* Used only when `$this->price_tier_transform_quantity_divide_by` is set. Possible values are: `up`, `down`
*/
price_tier_transform_quantity_round?: string | null;
is_domain_registration: boolean;
is_bundled: boolean;
is_sale_coupon_applied: boolean;
meta: string;
time_added_to_cart: number;
/**
* The billing term in days in numeric format, but as a string.
*
* Typically one of '31' (monthly), '365' (annual), or '730' (biennial).
*
* Similar to `months_per_bill_period`.
*/
bill_period: string;
/**
* The billing term in months in numeric format.
*
* Typically one of 1 (monthly), 12 (annual), or 24 (biennial).
*
* Similar to `bill_period`.
*/
months_per_bill_period: number | null;
volume: number;
quantity: number | null;
current_quantity: number | null;
extra: ResponseCartProductExtra;
item_tax: number;
item_tax_rate?: number;
product_type: string;
included_domain_purchase_amount: number;
/**
* True if the product is a renewal.
*
* This does not get set for `RequestCartProduct` which instead uses
* `extra.purchaseType` set to 'renewal'.
*/
is_renewal?: boolean;
/**
* True if the product is a renewal and the subscription will auto-renew.
*
* A subscription will auto-renew if it both can auto-renew (it's a recurring subscription,
* has a payment method, isn't blocked, etc.) and the user has auto-renew enabled.
*/
is_renewal_and_will_auto_renew?: boolean;
/**
* True if the product will not renew.
*/
is_one_time_purchase?: boolean;
subscription_id?: string;
introductory_offer_terms?: IntroductoryOfferTerms;
/**
* True if the cart item represents a purchase for a different user's site.
*/
is_gift_purchase?: boolean;
/**
* True if cart item is a domain that is included in a 100 Year Plan
*/
is_included_for_100yearplan: boolean;
/**
* If set, this is the ID of the payment method attached to the existing
* subscription for this product. This will only be set for renewals and
* only if the renewal has a payment method attached.
*/
stored_details_id?: string;
product_variants: ResponseCartProductVariant[];
}
export interface ResponseCartProductVariant {
product_id: number;
bill_period_in_months: number;
product_slug: string;
currency: string;
price_integer: number;
price_before_discounts_integer: number;
introductory_offer_discount_integer: number;
introductory_offer_terms:
| Record< string, never >
| Pick< IntroductoryOfferTerms, 'interval_unit' | 'interval_count' >;
volume?: number;
}
export interface ResponseCartCostOverride {
human_readable_reason: string;
new_subtotal_integer: number;
old_subtotal_integer: number;
override_code: string;
does_override_original_cost: boolean;
percentage: number;
first_unit_only: boolean;
}
export type IntroductoryOfferUnit = 'day' | 'week' | 'month' | 'year' | 'indefinite';
export interface IntroductoryOfferTerms {
/**
* True if the introductory offer is active on this product.
*/
enabled: boolean;
/**
* The unit that, when combined with `interval_count`, determines how long
* the introductory offer disount should be applied.
*/
interval_unit: IntroductoryOfferUnit;
/**
* The count that, when combined with `interval_unit`, determines how long
* the introductory offer lasts. eg: if `interval_count` is 3 and
* `interval_unit` is 'month', the discount lasts for 3 months (but always
* ends before the next renewal unless `transition_after_renewal_count` is
* set). If the `interval_unit` is 'month' and the product normally renews
* yearly, then the first renewal will be based on `interval_count` (eg:
* after 3 months) instead.
*
* Note that we sometimes renew products a 30 days before their expiry
* date, so in the above example, we would likely renew at the 2 month mark
* instead.
*/
interval_count: number;
/**
* If the introductory offer is not active (if `enabled` is false), the
* reason will probably be a human-readable reason why (although it may not
* exist even then).
*/
reason?: string;
/**
* The number of times the introductory offer cost and period will be used
* during renewals before using the regular cost and period. If this is 0,
* the discount will last just for the initial purchase; otherwise it will
* last for additional renewals also.
*/
transition_after_renewal_count: number;
/**
* True if the last discounted renewal will subtract the introductory offer
* period from the full period when calculating the price. For example: if
* you provide a 3 month free trial on a yearly plan, the first renewal
* would only cover 9 months (12 – 3 months). This reduced period is also
* reflected in the renewal price, as the user will only pay for the 9
* months instead of the full year.
*/
should_prorate_when_offer_ends: boolean;
}
export interface CartLocation {
countryCode?: string;
postalCode?: string;
subdivisionCode?: string;
vatId?: string;
organization?: string;
address?: string;
city?: string;
}
export type DomainLegalAgreementUrl = string;
export type DomainLegalAgreementTitle = string;
export type DomainLegalAgreements = Record< DomainLegalAgreementUrl, DomainLegalAgreementTitle >;
export interface ResponseCartProductExtra {
context?: string;
source?: string;
premium?: boolean;
new_quantity?: number;
domain_to_bundle?: string;
email_users?: TitanProductUser[];
google_apps_users?: GSuiteProductUser[];
google_apps_registration_data?: DomainContactDetails;
receipt_for_domain?: number;
domain_registration_agreement_url?: string;
legal_agreements?: never[] | DomainLegalAgreements;
is_gravatar_domain?: boolean;
/**
* Set to 'renewal' if requesting a renewal.
*
* Often this does not need to be explicitly set because the shopping cart
* endpoint will automatically make a requested product into a renewal if the
* product is already owned.
*
* This is not set for `ResponseCartProduct` objects which use `is_renewal`
* instead.
*/
purchaseType?: string;
afterPurchaseUrl?: string;
isJetpackCheckout?: boolean;
isAkismetSitelessCheckout?: boolean;
isMarketplaceSitelessCheckout?: boolean;
/**
* Marketplace properties
*
* These extra properties are always set for marketplace products.
* `product_slug` is for identifying the product, and `product_type` is for identifying the type of the product.
*/
is_marketplace_product?: boolean;
product_slug?: string;
product_type?: 'marketplace_plugin' | 'marketplace_theme' | 'saas_plugin';
/**
* True when:
* - the product has variants ( e.g. annual plan vs. monthly plan vs. multi-year plan )
* - we only want to show the single product selected by the user
* - we want to prevent the user from switching to a variant
*
* This will hide product variant UI elements in checkout ( line item variant dropdown or variant upsells )
*/
hideProductVariants?: boolean;
}
export interface ResponseCartGiftDetails {
receiver_blog_id: number;
receiver_blog_slug?: string;
receiver_blog_url?: string;
}
export interface RequestCartProductExtra extends ResponseCartProductExtra {
purchaseId?: string;
isAkismetSitelessCheckout?: boolean;
isJetpackCheckout?: boolean;
isMarketplaceSitelessCheckout?: boolean;
intentId?: number;
isGiftPurchase?: boolean;
jetpackSiteSlug?: string;
jetpackPurchaseToken?: string;
auth_code?: string;
privacy_available?: boolean;
selected_page_titles?: string[];
site_title?: string;
signup_flow?: string;
import_dns_records?: boolean;
signup?: boolean;
headstart_theme?: string;
feature_slug?: string;
is_hundred_year_domain?: boolean;
/**
* A way to signal intent to the back end when included as an extra with
* certain products.
*
* The only current usage is on Creator plan products that are bought
* on flow `/setup/site-migration`. If value `'migrate` is passed the
* Atomic DB will be created with UTF-8 encoding, which is a requirement
* for Migration Guru, our new tool for handling migrations. This extra
* can be removed once all migration flows are using Migration Guru.
*
*/
hosting_intent?: string;
}
export interface GSuiteProductUser {
firstname: string;
lastname: string;
email: string;
password: string;
recoveryEmail?: string;
}
export interface TitanProductUser {
alternative_email?: string;
email: string;
encrypted_password?: string;
is_admin?: boolean;
name?: string;
password?: string;
}
export type DomainContactDetails = {
firstName?: string;
lastName?: string;
organization?: string;
email?: string;
phone?: string;
address1?: string;
address2?: string;
city?: string;
state?: string;
postalCode?: string;
countryCode?: string;
fax?: string;
vatId?: string;
extra?: DomainContactDetailsExtra;
};
export type DomainContactDetailsExtra = {
ca?: CaDomainContactExtraDetails | null;
uk?: UkDomainContactExtraDetails | null;
fr?: FrDomainContactExtraDetails | null;
};
export type CaDomainContactExtraDetails = {
lang?: string;
legalType?: string;
ciraAgreementAccepted?: boolean;
};
export type UkDomainContactExtraDetails = {
registrantType?: string;
registrationNumber?: string;
tradingName?: string;
};
export type FrDomainContactExtraDetails = {
registrantType?: string;
registrantVatId?: string;
trademarkNumber?: string;
sirenSiret?: string;
};
export interface TermsOfServiceRecord {
key: string;
code: string;
args?: TermsOfServiceRecordArgsBase | TermsOfServiceRecordArgsRenewal;
}
export interface TermsOfServiceRecordArgsBase {
/**
* The date that the subscription will begin, formatted as a ISO 8601 date
* (eg: `2004-02-12T15:19:21+00:00`).
*/
subscription_start_date: string;
/**
* The `meta` value of the product (eg: its domain name). May be an empty
* string if there is no meta.
*/
product_meta: string;
/**
* The human readable name of the product.
*/
product_name: string;
/**
* The store product ID.
*/
product_id: number;
/**
* The price of the next renewal of this product. This may be based on the
* product's billing term (eg: in two years for a biennial plan) or the
* billing term of the introductory offer, if it differs (eg: in 3 months for
* a 3 month free trial of an annual plan).
*
* If an introductory offer applies for more than one renewal, this will be
* the price of the next renewal only, NOT the price of the renewal after the
* offer ends!
*
* This price is locale-formatted with a currency symbol.
* @deprecated use renewal_price_integer and format manually
*/
renewal_price: string;
/**
* The price of the next renewal of this product. This may be based on the
* product's billing term (eg: in two years for a biennial plan) or the
* billing term of the introductory offer, if it differs (eg: in 3 months for
* a 3 month free trial of an annual plan).
*
* If an introductory offer applies for more than one renewal, this will be
* the price of the next renewal only, NOT the price of the renewal after the
* offer ends!
*
* This price is an integer in the currency's smallest unit.
*/
renewal_price_integer: number;
/**
* The price of the product after the promotional pricing expires. If the
* next auto-renewal after the price expires would prorate the renewal price,
* this DOES NOT include that proration. See
* `maybe_prorated_regular_renewal_price_integer` for the price with that proration
* included.
*
* This price is locale-formatted with a currency symbol.
* @deprecated use regular_renewal_price_integer and format manually
*/
regular_renewal_price: string;
/**
* The price of the product after the promotional pricing expires. If the
* next auto-renewal after the price expires would prorate the renewal price,
* this DOES NOT include that proration. See
* `maybe_prorated_regular_renewal_price_integer` for the price with that proration
* included.
*
* This price is an integer in the currency's smallest unit.
*/
regular_renewal_price_integer: number;
/**
* The price of the product for the renewal immediately after the promotional
* pricing expires. If the next auto-renewal after the price expires would
* prorate the renewal price, this DOES include that proration. See
* `regular_renewal_price_integer` for the price without that proration
* included.
*
* This is the price that we will attempt to charge on
* `subscription_maybe_prorated_regular_auto_renew_date`.
*
* This price is an integer in the currency's smallest unit.
*/
maybe_prorated_regular_renewal_price_integer: number;
/**
* True if the product in the cart which has these terms is a manual renewal
* (as opposed to a new purchase or a quantity upgrade).
*/
is_renewal: boolean;
/**
* The number of auto-renewals after the current purchase completes which
* will be affected by the promotional pricing. If the product is affected by
* a prorated introductory offer, then the auto-renewal where the user will
* be charged the prorated price is not counted by this number.
*/
remaining_promotional_auto_renewals: number;
}
export interface TermsOfServiceRecordArgsRenewal extends TermsOfServiceRecordArgsBase {
/**
* The date that the promotional pricing will end, formatted as a ISO 8601
* date (eg: `2004-02-12T15:19:21+00:00`). This may be the date that an
* auto-renew will be attempted with the non-promotional price, but if the
* subscription renews earlier than the expiry date, the renewal may happen
* earlier than this date. See `subscription_regular_auto_renew_date` for
* the actual date of the non-promotional renewal.
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_end_of_promotion_date: string;
/**
* This date that an auto-renew will be attempted with the non-promotional
* possibly prorated price (`maybe_prorated_regular_renewal_price_integer`).
*
* This is ISO 8601 formatted (eg: `2004-02-12T15:19:21+00:00`).
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_maybe_prorated_regular_auto_renew_date: string;
/**
* This date that an auto-renew will be attempted with the non-promotional
* regular recurring price (`regular_renewal_price_integer`).
*
* This is ISO 8601 formatted (eg: `2004-02-12T15:19:21+00:00`).
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_regular_auto_renew_date: string;
/**
* The date when the product's subscription will expire if not renewed. This
* might be its renewal date, but it might not be since we often renew
* products earlier than their expiry date.
*
* This is ISO 8601 formatted (eg: `2004-02-12T15:19:21+00:00`).
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_expiry_date: string;
/**
* The date when the product's subscription will next automatically attempt a
* renewal. Note that this may not be the end of the promotional price, since
* some promotions apply to renewals also.
*
* This is ISO 8601 formatted (eg: `2004-02-12T15:19:21+00:00`).
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_auto_renew_date: string;
/**
* The number of days before the renewal attempt when the user will receive a
* pre-renewal reminder email.
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_pre_renew_reminder_days: number;
/**
* The number of pre-renewal emails the user will receive.
*
* Typically this is 1 or 0. For example, monthly subscriptions don't usually
* get a pre-renewal email.
*
* Only set if we can easily determine when the product will renew. Does not
* apply to domain transfers or multi-year domains.
*/
subscription_pre_renew_reminders_count: number;
}