2019-10-23 18:33:24 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
|
2020-05-12 18:16:04 +01:00
|
|
|
import {
|
|
|
|
AccountBalance,
|
|
|
|
CreditCard,
|
|
|
|
PaymentsApi,
|
2020-05-21 18:01:56 +01:00
|
|
|
PaymentsHistoryItem,
|
2020-05-12 18:16:04 +01:00
|
|
|
ProjectUsageAndCharges,
|
|
|
|
TokenDeposit,
|
|
|
|
} from '@/types/payments';
|
2019-10-23 18:33:24 +01:00
|
|
|
import { HttpClient } from '@/utils/httpClient';
|
2020-06-10 12:42:44 +01:00
|
|
|
import { Time } from '@/utils/time';
|
2019-10-23 18:33:24 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* PaymentsHttpApi is a http implementation of Payments API.
|
|
|
|
* Exposes all payments-related functionality
|
|
|
|
*/
|
|
|
|
export class PaymentsHttpApi implements PaymentsApi {
|
|
|
|
private readonly client: HttpClient = new HttpClient();
|
|
|
|
private readonly ROOT_PATH: string = '/api/v0/payments';
|
|
|
|
|
|
|
|
/**
|
2020-02-14 15:35:10 +00:00
|
|
|
* Get account balance.
|
2019-10-23 18:33:24 +01:00
|
|
|
*
|
|
|
|
* @returns balance in cents
|
|
|
|
* @throws Error
|
|
|
|
*/
|
2020-05-12 18:16:04 +01:00
|
|
|
public async getBalance(): Promise<AccountBalance> {
|
2019-10-23 18:33:24 +01:00
|
|
|
const path = `${this.ROOT_PATH}/account/balance`;
|
|
|
|
const response = await this.client.get(path);
|
|
|
|
|
2020-05-12 18:16:04 +01:00
|
|
|
if (!response.ok) {
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('Can not get account balance');
|
2019-10-23 18:33:24 +01:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:16:04 +01:00
|
|
|
const balance = await response.json();
|
|
|
|
if (balance) {
|
|
|
|
return new AccountBalance(balance.freeCredits, balance.coins);
|
2019-10-23 18:33:24 +01:00
|
|
|
}
|
|
|
|
|
2020-05-12 18:16:04 +01:00
|
|
|
return new AccountBalance();
|
2019-10-23 18:33:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-14 15:35:10 +00:00
|
|
|
* Try to set up a payment account.
|
2019-10-23 18:33:24 +01:00
|
|
|
*
|
|
|
|
* @throws Error
|
|
|
|
*/
|
|
|
|
public async setupAccount(): Promise<void> {
|
|
|
|
const path = `${this.ROOT_PATH}/account`;
|
|
|
|
const response = await this.client.post(path, null);
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('can not setup account');
|
|
|
|
}
|
|
|
|
|
2019-11-20 13:46:22 +00:00
|
|
|
/**
|
2020-03-26 13:34:16 +00:00
|
|
|
* projectsUsageAndCharges returns usage and how much money current user will be charged for each project which he owns.
|
2019-11-20 13:46:22 +00:00
|
|
|
*/
|
2020-03-26 13:34:16 +00:00
|
|
|
public async projectsUsageAndCharges(start: Date, end: Date): Promise<ProjectUsageAndCharges[]> {
|
2020-06-10 12:42:44 +01:00
|
|
|
const since = Time.toUnixTimestamp(start).toString();
|
|
|
|
const before = Time.toUnixTimestamp(end).toString();
|
2020-03-04 13:23:10 +00:00
|
|
|
const path = `${this.ROOT_PATH}/account/charges?from=${since}&to=${before}`;
|
2019-11-15 14:27:44 +00:00
|
|
|
const response = await this.client.get(path);
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('can not get projects charges');
|
|
|
|
}
|
|
|
|
|
|
|
|
const charges = await response.json();
|
2019-11-20 13:46:22 +00:00
|
|
|
if (charges) {
|
|
|
|
return charges.map(charge =>
|
2020-03-26 13:34:16 +00:00
|
|
|
new ProjectUsageAndCharges(
|
2020-03-04 13:23:10 +00:00
|
|
|
new Date(charge.since),
|
|
|
|
new Date(charge.before),
|
2019-11-20 13:46:22 +00:00
|
|
|
charge.egress,
|
2020-03-04 13:23:10 +00:00
|
|
|
charge.storage,
|
|
|
|
charge.objectCount,
|
|
|
|
charge.projectId,
|
|
|
|
charge.storagePrice,
|
|
|
|
charge.egressPrice,
|
2020-03-26 13:34:16 +00:00
|
|
|
charge.objectPrice,
|
|
|
|
),
|
2019-11-20 13:46:22 +00:00
|
|
|
);
|
|
|
|
}
|
2019-11-15 14:27:44 +00:00
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2019-10-23 18:33:24 +01:00
|
|
|
/**
|
2020-02-14 15:35:10 +00:00
|
|
|
* Add credit card.
|
|
|
|
*
|
2019-10-23 18:33:24 +01:00
|
|
|
* @param token - stripe token used to add a credit card as a payment method
|
|
|
|
* @throws Error
|
|
|
|
*/
|
|
|
|
public async addCreditCard(token: string): Promise<void> {
|
|
|
|
const path = `${this.ROOT_PATH}/cards`;
|
|
|
|
const response = await this.client.post(path, token);
|
|
|
|
|
2019-10-29 14:24:16 +00:00
|
|
|
if (response.ok) {
|
2019-10-23 18:33:24 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('can not add credit card');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Detach credit card from payment account.
|
2020-02-14 15:35:10 +00:00
|
|
|
*
|
2019-10-23 18:33:24 +01:00
|
|
|
* @param cardId
|
|
|
|
* @throws Error
|
|
|
|
*/
|
|
|
|
public async removeCreditCard(cardId: string): Promise<void> {
|
|
|
|
const path = `${this.ROOT_PATH}/cards/${cardId}`;
|
|
|
|
const response = await this.client.delete(path);
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('can not remove credit card');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-14 15:35:10 +00:00
|
|
|
* Get list of user`s credit cards.
|
2019-10-23 18:33:24 +01:00
|
|
|
*
|
|
|
|
* @returns list of credit cards
|
|
|
|
* @throws Error
|
|
|
|
*/
|
|
|
|
public async listCreditCards(): Promise<CreditCard[]> {
|
|
|
|
const path = `${this.ROOT_PATH}/cards`;
|
|
|
|
const response = await this.client.get(path);
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
throw new Error('can not list credit cards');
|
|
|
|
}
|
|
|
|
|
|
|
|
const creditCards = await response.json();
|
|
|
|
|
|
|
|
if (creditCards) {
|
|
|
|
return creditCards.map(card => new CreditCard(card.id, card.expMonth, card.expYear, card.brand, card.last4, card.isDefault));
|
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-14 15:35:10 +00:00
|
|
|
* Make credit card default.
|
|
|
|
*
|
2019-10-23 18:33:24 +01:00
|
|
|
* @param cardId
|
|
|
|
* @throws Error
|
|
|
|
*/
|
|
|
|
public async makeCreditCardDefault(cardId: string): Promise<void> {
|
|
|
|
const path = `${this.ROOT_PATH}/cards`;
|
|
|
|
const response = await this.client.patch(path, cardId);
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('can not make credit card default');
|
|
|
|
}
|
2019-10-31 16:56:54 +00:00
|
|
|
|
|
|
|
/**
|
2020-05-21 18:01:56 +01:00
|
|
|
* Returns a list of invoices, transactions and all others payments history items for payment account.
|
2019-10-31 16:56:54 +00:00
|
|
|
*
|
2020-05-21 18:01:56 +01:00
|
|
|
* @returns list of payments history items
|
2019-10-31 16:56:54 +00:00
|
|
|
* @throws Error
|
|
|
|
*/
|
2020-05-21 18:01:56 +01:00
|
|
|
public async paymentsHistory(): Promise<PaymentsHistoryItem[]> {
|
2019-10-31 16:56:54 +00:00
|
|
|
const path = `${this.ROOT_PATH}/billing-history`;
|
|
|
|
const response = await this.client.get(path);
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
throw new Error('can not list billing history');
|
|
|
|
}
|
|
|
|
|
2020-05-21 18:01:56 +01:00
|
|
|
const paymentsHistoryItems = await response.json();
|
|
|
|
if (paymentsHistoryItems) {
|
|
|
|
return paymentsHistoryItems.map(item =>
|
|
|
|
new PaymentsHistoryItem(
|
2019-10-31 16:56:54 +00:00
|
|
|
item.id,
|
|
|
|
item.description,
|
|
|
|
item.amount,
|
2019-12-27 16:41:43 +00:00
|
|
|
item.received,
|
2019-10-31 16:56:54 +00:00
|
|
|
item.status,
|
|
|
|
item.link,
|
|
|
|
new Date(item.start),
|
|
|
|
new Date(item.end),
|
2020-06-10 12:42:44 +01:00
|
|
|
item.type,
|
|
|
|
item.remaining,
|
|
|
|
),
|
2019-10-31 16:56:54 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
2019-11-25 12:59:41 +00:00
|
|
|
|
|
|
|
/**
|
2020-02-14 15:35:10 +00:00
|
|
|
* makeTokenDeposit process coin payments.
|
|
|
|
*
|
2019-11-25 12:59:41 +00:00
|
|
|
* @param amount
|
|
|
|
* @throws Error
|
|
|
|
*/
|
|
|
|
public async makeTokenDeposit(amount: number): Promise<TokenDeposit> {
|
|
|
|
const path = `${this.ROOT_PATH}/tokens/deposit`;
|
|
|
|
const response = await this.client.post(path, JSON.stringify({ amount }));
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
if (response.status === 401) {
|
|
|
|
throw new ErrorUnauthorized();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error('can not process coin payment');
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
2019-12-12 15:18:47 +00:00
|
|
|
return new TokenDeposit(result.amount, result.address, result.link);
|
2019-11-25 12:59:41 +00:00
|
|
|
}
|
2019-10-23 18:33:24 +01:00
|
|
|
}
|