diff --git a/src/api/ApiContext.ts b/src/api/ApiContext.ts index cdbce30..95c4d78 100644 --- a/src/api/ApiContext.ts +++ b/src/api/ApiContext.ts @@ -11,6 +11,9 @@ import { DailyForecasts, SubscriptionItems, SubscriptionCheckout, + SubscriptionStatus, + SubscriptionReceipts, + PaymentIntents, } from './resources' export interface ApiContextValue { @@ -25,6 +28,10 @@ export interface ApiContextValue { getAuras: ReturnType> getSubscriptionItems: ReturnType> getSubscriptionCheckout: ReturnType> + getSubscriptionStatus: ReturnType>, + getSubscriptionReceipt: ReturnType>, + createSubscriptionReceipt: ReturnType>, + createPaymentIntent: ReturnType> } export const ApiContext = createContext({} as ApiContextValue) diff --git a/src/api/api.ts b/src/api/api.ts index 8060bfa..c3f4d29 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -11,6 +11,9 @@ import { DailyForecasts, SubscriptionItems, SubscriptionCheckout, + SubscriptionReceipts, + SubscriptionStatus, + PaymentIntents } from './resources' const api = { @@ -25,6 +28,10 @@ const api = { getAuras: createMethod(Auras.createRequest), getSubscriptionItems: createMethod(SubscriptionItems.createRequest), getSubscriptionCheckout: createMethod(SubscriptionCheckout.createRequest), + getSubscriptionStatus: createMethod(SubscriptionStatus.createRequest), + getSubscriptionReceipt: createMethod(SubscriptionReceipts.createGetRequest), + createSubscriptionReceipt: createMethod(SubscriptionReceipts.createRequest), + createPaymentIntent: createMethod(PaymentIntents.createRequest) } export function createApi(): ApiContextValue { diff --git a/src/api/resources/AssetCategories.ts b/src/api/resources/AssetCategories.ts index c7e1dbb..dc22983 100644 --- a/src/api/resources/AssetCategories.ts +++ b/src/api/resources/AssetCategories.ts @@ -1,10 +1,9 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { getAuthHeaders } from "../utils" -export interface Payload { +export interface Payload extends AuthPayload { locale: string - token: AuthToken } export interface Response { diff --git a/src/api/resources/Assets.ts b/src/api/resources/Assets.ts index 538b738..ab9ce41 100644 --- a/src/api/resources/Assets.ts +++ b/src/api/resources/Assets.ts @@ -1,10 +1,9 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { AssetCategory } from "./AssetCategories" import { getAuthHeaders } from "../utils" -export interface Payload { - token: AuthToken +export interface Payload extends AuthPayload { category: string page?: number perPage?: number diff --git a/src/api/resources/Auras.ts b/src/api/resources/Auras.ts index 9bbd89d..8957b8e 100644 --- a/src/api/resources/Auras.ts +++ b/src/api/resources/Auras.ts @@ -1,10 +1,8 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { getAuthHeaders } from "../utils" -export interface Payload { - token: AuthToken -} +export type Payload = AuthPayload export interface Response { user_aura: UserAura diff --git a/src/api/resources/User.ts b/src/api/resources/User.ts index dda2cb8..8ebe9b3 100644 --- a/src/api/resources/User.ts +++ b/src/api/resources/User.ts @@ -1,12 +1,10 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { getAuthHeaders } from "../utils" -export interface GetPayload { - token: AuthToken -} +export type GetPayload = AuthPayload -export interface PatchPayload extends GetPayload { +export interface PatchPayload extends AuthPayload { user: Partial } diff --git a/src/api/resources/UserDailyForecasts.ts b/src/api/resources/UserDailyForecasts.ts index b511fdd..02bf673 100644 --- a/src/api/resources/UserDailyForecasts.ts +++ b/src/api/resources/UserDailyForecasts.ts @@ -1,10 +1,8 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { getAuthHeaders } from "../utils" -export interface Payload { - token: AuthToken -} +export type Payload = AuthPayload export interface Response { user_daily_forecast: UserDailyForecast diff --git a/src/api/resources/UserPaymentIntents.ts b/src/api/resources/UserPaymentIntents.ts new file mode 100644 index 0000000..8fa9f7b --- /dev/null +++ b/src/api/resources/UserPaymentIntents.ts @@ -0,0 +1,59 @@ +import routes from "../../routes" +import { AuthPayload } from "../types" +import { getAuthHeaders } from "../utils" + +export interface Payload extends AuthPayload { + paymentMethod: PaymentMethod + currencyCode: CurrencyCode +} + +export interface Response { + payment_intent: PaymentIntent + customer: Customer +} + +export type PaymentMethod = 'apple_pay' | 'google_pay' | 'card' +export type CurrencyCode = 'USD' +export interface PaymentIntent { + id: string + status: 'inited' | 'in_progress' | 'authorized' | 'consumed' | 'expired' + amount: number + gateway_account_id: string + payment_method_type: PaymentMethod + expires_at: number + created_at: number + modified_at: number + currency_code: CurrencyCode + gateway: string +} +export interface Customer { + id: string + email: string + auto_collection: 'on' | 'off' + net_term_days: number + allow_direct_debit: boolean + taxability: 'taxable' | 'exempt' + created_at: number + updated_at: number + pii_cleared: 'active' | 'scheduled_for_clear' | 'cleared' + channel: 'web' | 'app_store' | 'play_store' + resource_version: number + deleted: boolean + card_status: 'no_card' | 'valid' | 'expiring' | 'expired' + promotional_credits: number + refundable_credits: number + excess_payments: number + unbilled_charges: number + preferred_currency_code: CurrencyCode +} + +export const createRequest = ({ token, paymentMethod, currencyCode }: Payload): Request => { + const url = new URL(routes.server.paymentIntents()) + const body = JSON.stringify({ + payment_intent: { + payment_method_type: paymentMethod, + currency_code: currencyCode, + } + }) + return new Request(url, { method: 'POST', headers: getAuthHeaders(token), body }) +} diff --git a/src/api/resources/UserSubscriptionCheckout.ts b/src/api/resources/UserSubscriptionCheckout.ts index 6cf417f..624a3ca 100644 --- a/src/api/resources/UserSubscriptionCheckout.ts +++ b/src/api/resources/UserSubscriptionCheckout.ts @@ -1,9 +1,8 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { getAuthHeaders } from "../utils" -export interface Payload { - token: AuthToken +export interface Payload extends AuthPayload { embed?: boolean locale: string itemPriceId: string diff --git a/src/api/resources/UserSubscriptionItemPrices.ts b/src/api/resources/UserSubscriptionItemPrices.ts index 9af780f..d177a21 100644 --- a/src/api/resources/UserSubscriptionItemPrices.ts +++ b/src/api/resources/UserSubscriptionItemPrices.ts @@ -1,10 +1,9 @@ import routes from "../../routes" -import { AuthToken } from "../types" +import { AuthPayload } from "../types" import { getAuthHeaders } from "../utils" -export interface Payload { +export interface Payload extends AuthPayload { locale: string - token: AuthToken } export interface Response { diff --git a/src/api/resources/UserSubscriptionReceipts.ts b/src/api/resources/UserSubscriptionReceipts.ts new file mode 100644 index 0000000..efb6636 --- /dev/null +++ b/src/api/resources/UserSubscriptionReceipts.ts @@ -0,0 +1,83 @@ +import routes from "../../routes" +import { AuthPayload } from "../types" +import { getAuthHeaders } from "../utils" + +export interface GetPayload extends AuthPayload { + id: string +} + +export interface ChargebeeReceiptPayload extends AuthPayload { + itemPriceId: string + gwToken: string + referenceId: string +} + +export interface AppleReceiptPayload extends AuthPayload { + receiptData: string + autorenewable: boolean + sandbox: boolean +} + +export type Payload = ChargebeeReceiptPayload | AppleReceiptPayload + +export interface Response { + subscription_receipt: { + id: string + user_id: number + status: number + expires_at: null | string + requested_at: string + created_at: string + data: { + input: { + subscription_items: [ + { + item_price_id: string + } + ], + payment_intent: { + gw_token: string + gateway_account_id: string + } + }, + app_bundle_id: string + autorenewable: boolean + error: string + } + } +} + +function createRequest({ token, itemPriceId, gwToken, referenceId }: ChargebeeReceiptPayload): Request +function createRequest({ token, receiptData, autorenewable, sandbox }: AppleReceiptPayload): Request +function createRequest(payload: Payload): Request +function createRequest(payload: Payload): Request { + const url = new URL(routes.server.subscriptionReceipts()) + const data = isChargebeeReceipt(payload) ? { + way: 'chargebee', + subscription_receipt: { + item_price_id: payload.itemPriceId, + gw_token: payload.gwToken, + reference_id: payload.referenceId, + } + } : { + way: 'apple', + subscription_receipt: { + receipt_data: payload.receiptData, + autorenewable: payload.autorenewable, + sandbox: payload.sandbox, + } + } + const body = JSON.stringify(data) + return new Request(url, { method: 'POST', headers: getAuthHeaders(payload.token), body }) +} + +function isChargebeeReceipt(payload: Payload ): payload is ChargebeeReceiptPayload { + return 'itemPriceId' in payload && 'gwToken' in payload && 'referenceId' in payload +} + +function createGetRequest({ id, token }: GetPayload): Request { + const url = new URL(routes.server.subscriptionReceipt(id)) + return new Request(url, { method: 'GET', headers: getAuthHeaders(token) }) +} + +export { createRequest, createGetRequest } diff --git a/src/api/resources/UserSubscriptionStatus.ts b/src/api/resources/UserSubscriptionStatus.ts new file mode 100644 index 0000000..6ec0b1c --- /dev/null +++ b/src/api/resources/UserSubscriptionStatus.ts @@ -0,0 +1,16 @@ +import routes from "../../routes" +import { AuthPayload } from "../types" +import { getAuthHeaders } from "../utils" + +export type Payload = AuthPayload + +export interface Response { + user: { + has_subscription: boolean + } +} + +export const createRequest = ({ token }: Payload): Request => { + const url = new URL(routes.server.subscriptionStatus()) + return new Request(url, { method: 'GET', headers: getAuthHeaders(token) }) +} diff --git a/src/api/resources/index.ts b/src/api/resources/index.ts index 9842901..a1a1486 100644 --- a/src/api/resources/index.ts +++ b/src/api/resources/index.ts @@ -8,3 +8,6 @@ export * as Elements from './Elements' export * as AuthTokens from './AuthTokens' export * as SubscriptionItems from './UserSubscriptionItemPrices' export * as SubscriptionCheckout from './UserSubscriptionCheckout' +export * as SubscriptionStatus from './UserSubscriptionStatus' +export * as SubscriptionReceipts from './UserSubscriptionReceipts' +export * as PaymentIntents from './UserPaymentIntents' diff --git a/src/api/types.ts b/src/api/types.ts index cc83771..a1bd540 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -16,3 +16,7 @@ export interface ErrorResponse { } export type AuthToken = string + +export interface AuthPayload { + token: AuthToken +} diff --git a/src/routes.ts b/src/routes.ts index 904f702..c2e8ba6 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -24,8 +24,12 @@ const routes = { assetCategories: () => [apiHost, prefix, 'assets', 'categories.json'].join('/'), dailyForecasts: () => [apiHost, prefix, 'user', 'daily_forecast.json'].join('/'), auras: () => [apiHost, prefix, 'user', 'aura.json'].join('/'), + paymentIntents: () => [apiHost, prefix, 'user', 'payment_intents.json'].join('/'), subscriptionItems: () => [apiHost, prefix, 'user', 'subscription', 'item_prices.json'].join('/'), subscriptionCheckout: () => [apiHost, prefix, 'user', 'subscription', 'checkout', 'new.json'].join('/'), + subscriptionStatus: () => [apiHost, prefix, 'user', 'subscription_receipts', 'status.json'].join('/'), + subscriptionReceipts: () => [apiHost, prefix, 'user', 'subscription_receipts.json'].join('/'), + subscriptionReceipt: (id: string) => [apiHost, prefix, 'user', 'subscription_receipts', `${id}.json`].join('/'), }, }