2021-03-09 16:23:13 +00:00
|
|
|
import { getEndpoint } from 'utils/common/apiUtil';
|
|
|
|
import HTTPService from './HTTPService';
|
|
|
|
const ENDPOINT = getEndpoint();
|
2021-03-12 09:12:19 +00:00
|
|
|
import { getToken } from 'utils/common/key';
|
2021-04-07 07:18:45 +00:00
|
|
|
import { runningInBrowser } from 'utils/common/';
|
2021-03-17 07:21:55 +00:00
|
|
|
import { setData, LS_KEYS } from 'utils/storage/localStorage';
|
|
|
|
import { convertBytesToGBs } from 'utils/billingUtil';
|
2021-04-06 13:43:11 +00:00
|
|
|
import { loadStripe, Stripe } from '@stripe/stripe-js';
|
|
|
|
|
|
|
|
enum PAYMENT_INTENT_STATUS {
|
|
|
|
SUCCEEDED = 'succeeded',
|
|
|
|
REQUIRE_ACTION = 'requires_action',
|
|
|
|
REQUIRE_PAYMENT_METHOD = 'requires_payment_method',
|
|
|
|
}
|
2021-03-11 16:31:20 +00:00
|
|
|
export interface Subscription {
|
|
|
|
id: number;
|
|
|
|
userID: number;
|
|
|
|
productID: string;
|
|
|
|
storage: number;
|
|
|
|
originalTransactionID: string;
|
|
|
|
expiryTime: number;
|
|
|
|
paymentProvider: string;
|
2021-03-18 13:48:44 +00:00
|
|
|
isCancelled: boolean;
|
2021-03-11 16:31:20 +00:00
|
|
|
}
|
2021-03-12 12:30:33 +00:00
|
|
|
export interface Plan {
|
|
|
|
androidID: string;
|
|
|
|
iosID: string;
|
|
|
|
storage: number;
|
|
|
|
price: string;
|
|
|
|
period: string;
|
2021-03-12 15:09:17 +00:00
|
|
|
stripeID: string;
|
2021-03-12 12:30:33 +00:00
|
|
|
}
|
2021-03-21 04:49:37 +00:00
|
|
|
|
2021-04-06 13:43:11 +00:00
|
|
|
export interface SubscriptionUpdateResponse {
|
|
|
|
subscription: Subscription;
|
|
|
|
status: PAYMENT_INTENT_STATUS;
|
|
|
|
clientSecret: string;
|
2021-03-21 04:49:37 +00:00
|
|
|
}
|
2021-03-17 07:21:55 +00:00
|
|
|
export const FREE_PLAN = 'free';
|
2021-03-18 15:22:34 +00:00
|
|
|
class billingService {
|
2021-04-06 13:43:11 +00:00
|
|
|
private stripe: Stripe;
|
2021-03-12 22:48:44 +00:00
|
|
|
constructor() {
|
2021-03-09 16:23:13 +00:00
|
|
|
let publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
|
2021-04-06 13:43:11 +00:00
|
|
|
const main = async () => {
|
|
|
|
this.stripe = await loadStripe(publishableKey);
|
|
|
|
};
|
|
|
|
runningInBrowser() && main();
|
2021-03-09 16:23:13 +00:00
|
|
|
}
|
2021-03-17 07:21:55 +00:00
|
|
|
public async updatePlans() {
|
2021-03-12 12:30:33 +00:00
|
|
|
try {
|
|
|
|
const response = await HTTPService.get(`${ENDPOINT}/billing/plans`);
|
2021-03-17 07:21:55 +00:00
|
|
|
const plans = response.data['plans'];
|
|
|
|
setData(LS_KEYS.PLANS, plans);
|
2021-03-12 12:30:33 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.error('failed to get plans', e);
|
|
|
|
}
|
|
|
|
}
|
2021-03-17 07:21:55 +00:00
|
|
|
public async syncSubscription() {
|
2021-03-13 12:55:47 +00:00
|
|
|
try {
|
|
|
|
const response = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/billing/subscription`,
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
const subscription = response.data['subscription'];
|
|
|
|
setData(LS_KEYS.SUBSCRIPTION, subscription);
|
|
|
|
} catch (e) {
|
|
|
|
console.error(`failed to get user's subscription details`, e);
|
|
|
|
}
|
|
|
|
}
|
2021-03-12 17:34:20 +00:00
|
|
|
public async buySubscription(productID) {
|
2021-03-09 16:23:13 +00:00
|
|
|
try {
|
2021-03-12 22:48:44 +00:00
|
|
|
const response = await this.createCheckoutSession(productID);
|
2021-03-12 11:43:47 +00:00
|
|
|
await this.stripe.redirectToCheckout({
|
2021-03-12 15:09:17 +00:00
|
|
|
sessionId: response.data['sessionID'],
|
2021-03-09 16:23:13 +00:00
|
|
|
});
|
|
|
|
} catch (e) {
|
2021-03-12 17:34:20 +00:00
|
|
|
console.error('unable to buy subscription', e);
|
2021-03-17 07:21:55 +00:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public async updateSubscription(productID) {
|
|
|
|
try {
|
|
|
|
const response = await HTTPService.post(
|
|
|
|
`${ENDPOINT}/billing/stripe/update-subscription`,
|
|
|
|
{
|
|
|
|
productID,
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
|
|
|
}
|
|
|
|
);
|
2021-04-06 13:43:11 +00:00
|
|
|
const subscriptionUpdateResponse: SubscriptionUpdateResponse =
|
|
|
|
response.data['subscriptionUpdateResponse'];
|
|
|
|
switch (subscriptionUpdateResponse.status) {
|
|
|
|
case PAYMENT_INTENT_STATUS.SUCCEEDED:
|
|
|
|
await this.acknowledgeSubscriptionUpdate();
|
|
|
|
break;
|
|
|
|
case PAYMENT_INTENT_STATUS.REQUIRE_PAYMENT_METHOD:
|
|
|
|
throw new Error(
|
|
|
|
PAYMENT_INTENT_STATUS.REQUIRE_PAYMENT_METHOD
|
|
|
|
);
|
|
|
|
case PAYMENT_INTENT_STATUS.REQUIRE_ACTION:
|
|
|
|
const { error } = await this.stripe.confirmCardPayment(
|
|
|
|
subscriptionUpdateResponse.clientSecret
|
|
|
|
);
|
|
|
|
if (error) {
|
|
|
|
throw error;
|
|
|
|
} else {
|
|
|
|
await this.acknowledgeSubscriptionUpdate();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2021-03-17 07:21:55 +00:00
|
|
|
} catch (e) {
|
2021-03-18 15:18:29 +00:00
|
|
|
console.error(e);
|
2021-03-18 13:48:44 +00:00
|
|
|
throw e;
|
2021-04-06 13:43:11 +00:00
|
|
|
} finally {
|
|
|
|
await this.syncSubscription();
|
2021-03-17 07:21:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public async cancelSubscription() {
|
|
|
|
try {
|
|
|
|
const response = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/billing/stripe/cancel-subscription`,
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
|
|
|
}
|
|
|
|
);
|
2021-03-18 13:48:44 +00:00
|
|
|
const subscription = response.data['subscription'];
|
|
|
|
setData(LS_KEYS.SUBSCRIPTION, subscription);
|
2021-03-17 07:21:55 +00:00
|
|
|
} catch (e) {
|
2021-03-18 15:18:29 +00:00
|
|
|
console.error(e);
|
2021-03-17 12:07:20 +00:00
|
|
|
throw e;
|
2021-03-09 16:23:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-12 22:48:44 +00:00
|
|
|
private async createCheckoutSession(productID) {
|
2021-03-13 10:04:17 +00:00
|
|
|
return HTTPService.post(
|
2021-03-17 07:21:55 +00:00
|
|
|
`${ENDPOINT}/billing/stripe/create-checkout-session`,
|
2021-03-13 10:04:17 +00:00
|
|
|
{
|
|
|
|
productID,
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
|
|
|
}
|
|
|
|
);
|
2021-03-09 16:23:13 +00:00
|
|
|
}
|
2021-03-10 03:25:00 +00:00
|
|
|
|
2021-03-12 17:34:20 +00:00
|
|
|
public async verifySubscription(sessionID): Promise<Subscription> {
|
2021-03-10 03:25:00 +00:00
|
|
|
try {
|
2021-03-12 17:34:20 +00:00
|
|
|
const response = await HTTPService.post(
|
|
|
|
`${ENDPOINT}/billing/verify-subscription`,
|
2021-03-12 11:43:47 +00:00
|
|
|
{
|
2021-03-12 17:34:20 +00:00
|
|
|
paymentProvider: 'stripe',
|
2021-03-12 22:48:44 +00:00
|
|
|
productID: null,
|
2021-03-12 17:34:20 +00:00
|
|
|
VerificationData: sessionID,
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
2021-03-12 11:43:47 +00:00
|
|
|
}
|
2021-03-10 03:25:00 +00:00
|
|
|
);
|
2021-03-13 12:55:47 +00:00
|
|
|
const subscription = response.data['subscription'];
|
|
|
|
setData(LS_KEYS.SUBSCRIPTION, subscription);
|
|
|
|
return subscription;
|
2021-03-10 03:25:00 +00:00
|
|
|
} catch (err) {
|
2021-03-12 17:34:20 +00:00
|
|
|
console.error('Error while verifying subscription', err);
|
2021-03-10 03:25:00 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-12 17:34:20 +00:00
|
|
|
|
|
|
|
public async redirectToCustomerPortal() {
|
2021-03-12 23:42:19 +00:00
|
|
|
try {
|
|
|
|
const response = await HTTPService.get(
|
2021-04-08 04:56:37 +00:00
|
|
|
`${ENDPOINT}/billing/stripe/customer-portal`,
|
2021-03-12 23:42:19 +00:00
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
|
|
|
}
|
|
|
|
);
|
2021-03-16 12:52:57 +00:00
|
|
|
window.location.href = response.data['url'];
|
2021-03-12 23:42:19 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.error('unable to get customer portal url');
|
2021-04-08 05:08:15 +00:00
|
|
|
throw e;
|
2021-03-12 23:42:19 +00:00
|
|
|
}
|
2021-03-12 17:34:20 +00:00
|
|
|
}
|
2021-04-06 13:43:11 +00:00
|
|
|
public async getUsage() {
|
2021-03-11 16:04:52 +00:00
|
|
|
try {
|
|
|
|
const response = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/billing/usage`,
|
|
|
|
{ startTime: 0, endTime: Date.now() * 1000 },
|
|
|
|
{
|
2021-03-12 03:16:35 +00:00
|
|
|
'X-Auth-Token': getToken(),
|
2021-03-11 16:04:52 +00:00
|
|
|
}
|
|
|
|
);
|
2021-03-17 07:21:55 +00:00
|
|
|
return convertBytesToGBs(response.data.usage);
|
2021-03-11 16:04:52 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.error('error getting usage', e);
|
|
|
|
}
|
|
|
|
}
|
2021-04-06 13:43:11 +00:00
|
|
|
private async acknowledgeSubscriptionUpdate() {
|
|
|
|
try {
|
|
|
|
await HTTPService.post(
|
|
|
|
`${ENDPOINT}/billing/stripe/acknowledge-subscription-update`,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': getToken(),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} catch (e) {
|
|
|
|
console.error('error acknowledging subscription update', e);
|
|
|
|
}
|
|
|
|
}
|
2021-03-09 16:23:13 +00:00
|
|
|
}
|
|
|
|
|
2021-03-18 15:22:34 +00:00
|
|
|
export default new billingService();
|