UNPKG

stripe-stateful-mock

Version:
232 lines (203 loc) 9.4 kB
import Stripe from "stripe"; import {AccountData} from "./AccountData"; import {RestError} from "./RestError"; import {applyListOptions, generateId, stringifyMetadata} from "./utils"; import {customers} from "./customers"; import {plans} from "./plans"; import {prices} from "./prices"; import {verify} from "./verify"; import {taxRates} from "./taxRates"; import {accounts} from "./accounts"; import log = require("loglevel"); export namespace subscriptions { const accountSubscriptions = new AccountData<Stripe.Subscription>(); const accountSubscriptionItems = new AccountData<Stripe.SubscriptionItem>(); export function create(accountId: string, params: Stripe.SubscriptionCreateParams): Stripe.Subscription { log.debug("subscriptions.create", accountId, params); let default_source: string; const paramsDefaultSource = params.default_source; if (paramsDefaultSource && typeof paramsDefaultSource !== "string") { const customer = params.customer; const card = customers.createCard(accountId, customer, { source: paramsDefaultSource }); default_source = card.id; } else if (typeof paramsDefaultSource === "string") { default_source = paramsDefaultSource; } const subscriptionId = (params as any).id || `sub_${generateId(14)}`; if (accountSubscriptions.contains(accountId, subscriptionId)) { throw new RestError(400, { code: "resource_already_exists", doc_url: "https://stripe.com/docs/error-codes/resource-already-exists", message: "Subscription already exists.", type: "invalid_request_error" }); } const now = Math.floor((Date.now() / 1000)); const nextMonth = new Date(); nextMonth.setMonth(nextMonth.getMonth() + 1); const subscription: Stripe.Subscription = { id: subscriptionId, object: "subscription", application_fee_percent: +params.application_fee_percent || null, collection_method: params.collection_method || "charge_automatically", billing_cycle_anchor: +params.billing_cycle_anchor || now, billing_thresholds: params.billing_thresholds ? { amount_gte: params.billing_thresholds.amount_gte ?? null, reset_billing_cycle_anchor: params.billing_thresholds.reset_billing_cycle_anchor ?? null } : null, cancel_at: null, cancel_at_period_end: false, canceled_at: null, created: now, current_period_end: Math.floor(nextMonth.getTime() / 1000), // Hard coded to assume month long subscriptions current_period_start: now, customer: params.customer, days_until_due: +params.days_until_due || null, default_payment_method: null, default_source: default_source || null, default_tax_rates: (params.default_tax_rates || null)?.map(t => taxRates.retrieve(accountId, t, "default_tax_rate")), discount: null, ended_at: null, items: { object: "list", data: [], has_more: false, url: `/v1/subscription_items?subscription=${subscriptionId}` }, latest_invoice: `in_${generateId(14)}`, livemode: false, metadata: stringifyMetadata(params.metadata), next_pending_invoice_item_invoice: null, pause_collection: null, pending_invoice_item_interval: null, pending_setup_intent: null, pending_update: null, schedule: null, start_date: Math.floor(Date.now() / 1000), status: "active", transfer_data: params.transfer_data ? { amount_percent: params.transfer_data.amount_percent ?? null, destination: accounts.retrieve(accountId, params.transfer_data.destination, "") } : null, trial_end: null, trial_start: null }; if (params.items) { for (const item of params.items) { subscription.items.data.push( createItem(accountId, item, subscription.id) ); } } accountSubscriptions.put(accountId, subscription); customers.addSubscription( accountId, typeof subscription.customer === "string" ? subscription.customer : subscription.customer.id, subscription ); return subscription; } function getOrCreatePlan(accountId: string, planId: string): Stripe.Plan { try { return plans.retrieve(accountId, planId, "plan"); } catch (error) { if ((error as RestError).error?.code === "resource_missing") { return plans.create(accountId, { id: planId, currency: "usd", interval: "month" }); } throw error; } } function createItem(accountId: string, item: Stripe.SubscriptionCreateParams.Item, subscriptionId: string): Stripe.SubscriptionItem { const paramId = (item as any).id; const subItemId = paramId || `si_${generateId(14)}`; const subscriptionItem: Stripe.SubscriptionItem = { object: "subscription_item", id: subItemId, billing_thresholds: item.billing_thresholds || null, created: Math.floor(Date.now() / 1000), deleted: undefined, metadata: stringifyMetadata(item.metadata), plan: getOrCreatePlan(accountId, item.plan), // isn't in the documentation, deprecated? price: item.price ? prices.retrieve(accountId, item.price, "price") : null, quantity: +item.quantity || 1, subscription: subscriptionId, tax_rates: (item.tax_rates || null)?.map(r => taxRates.retrieve(accountId, r, "tax_rate")) }; accountSubscriptionItems.put(accountId, subscriptionItem); return subscriptionItem; } export function updateItem(accountId: string, subscriptionItemId: string, params: Stripe.SubscriptionItemUpdateParams): Stripe.SubscriptionItem { log.debug("subscriptions.updateItem", accountId, subscriptionItemId, params); const subscriptionItem = retrieveItem(accountId, subscriptionItemId, "id"); if (params.quantity) { subscriptionItem.quantity = +params.quantity; } return subscriptionItem; } export function retrieve(accountId: string, subscriptionId: string, paramName: string): Stripe.Subscription { log.debug("subscriptions.retrieve", subscriptionId); const subscription = accountSubscriptions.get( accountId, subscriptionId ); if (!subscription) { throw new RestError(404, { code: "resource_missing", doc_url: "https://stripe.com/docs/error-codes/resource-missing", message: `No such subscription: ${subscriptionId}`, param: paramName, type: "invalid_request_error" }); } return subscription; } export function retrieveItem(accountId: string, subscriptionItemId: string, paramName: string): Stripe.SubscriptionItem { log.debug("subscriptions.retrieveItem", subscriptionItemId); const subscriptionItem = accountSubscriptionItems.get( accountId, subscriptionItemId ); if (!subscriptionItem) { throw new RestError(404, { code: "resource_missing", doc_url: "https://stripe.com/docs/error-codes/resource-missing", message: `No such subscription_item: ${subscriptionItemId}`, param: paramName, type: "invalid_request_error" }); } return subscriptionItem; } export function list(accountId: string, params: Stripe.SubscriptionListParams): Stripe.ApiList<Stripe.Subscription> { log.debug("subscriptions.list", params); let data = accountSubscriptions.getAll(accountId); if (params.customer) { data = data.filter(d => { if (typeof d.customer === "string") { return d.customer === params.customer; } else { return d.customer.id === params.customer; } }); } return applyListOptions(data, params, (id, paramName) => { return retrieve(accountId, id, paramName); }); } export function listItems(accountId: string, params: Partial<Stripe.SubscriptionItemListParams>): Stripe.ApiList<Stripe.SubscriptionItem> { log.debug("subscriptionItems.list", params); verify.requiredParams(params, ["subscription"]); const data = accountSubscriptionItems .getAll(accountId) .filter(d => { return d.subscription === params.subscription; }); return applyListOptions(data, params, (id, paramName) => { return retrieveItem(accountId, id, paramName); }); } }