added list of payment methods
added deletion
construction: check in the back user can not delete pm he doesn't own ?
construction: adapt authorizations
MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -95,8 +95,8 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
{ resource: Entity.USERS_SITES, action: [Action.ASSIGN, Action.UNASSIGN], attributes: ['*'] },
{ resource: Entity.PAYMENT_METHODS, action: Action.LIST, attributes: ['*'] },
{
- resource: Entity.PAYMENT_METHOD, action: [Action.READ, Action.CREATE], attributes: ['*'],
- condition: { Fn: 'EQUALS', args: { 'user': '$.owner' } }
+ resource: Entity.PAYMENT_METHOD, action: [Action.READ, Action.CREATE, Action.DELETE], attributes: ['*'],
+ // condition: { Fn: 'EQUALS', args: { 'user': '$.owner' } }
},
]
},
@@ -209,7 +209,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
},
{ resource: Entity.NOTIFICATION, action: Action.CREATE, attributes: ['*'] },
{
- resource: Entity.PAYMENT_METHOD, action: [Action.READ, Action.CREATE], attributes: ['*'],
+ resource: Entity.PAYMENT_METHOD, action: [Action.READ, Action.CREATE, Action.DELETE], attributes: ['*'],
condition: { Fn: 'EQUALS', args: { 'user': '$.owner' } }
},
]

MODIFY
src/integration/billing/BillingIntegration.ts
src/integration/billing/BillingIntegration.ts
-import { BillingChargeInvoiceAction, BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingInvoiceStatus, BillingOperationResult, BillingStatus, BillingTax, BillingUser, BillingUserSynchronizeAction } from '../../types/Billing';
+import { BillingChargeInvoiceAction, BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingInvoiceStatus, BillingMethod, BillingOperationResult, BillingPaymentMethod, BillingPaymentMethodResult, BillingStatus, BillingTax, BillingUser, BillingUserSynchronizeAction } from '../../types/Billing';
import User, { UserStatus } from '../../types/User';
import BackendError from '../../exception/BackendError';
import { BillingSetting } from '../../types/Setting';
import BillingStorage from '../../storage/mongodb/BillingStorage';
import Constants from '../../utils/Constants';
+import { DataResult } from '../../types/DataResult';
import Logging from '../../utils/Logging';
import NotificationHandler from '../../notification/NotificationHandler';
import { Request } from 'express';
@@ -580,4 +581,7 @@ export default abstract class BillingIntegration<T extends BillingSetting> {
abstract consumeBillingEvent(req: Request): Promise<boolean>;
+ abstract paymentMethodsList(user: User): Promise<BillingPaymentMethodResult>;
+
+ abstract deletePaymentMethod(user: User, paymentMethodId: string): Promise<BillingPaymentMethodResult>;
}

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
/* eslint-disable @typescript-eslint/member-ordering */
-import { BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingInvoiceStatus, BillingOperationResult, BillingStatus, BillingTax, BillingUser } from '../../../types/Billing';
+import { BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceDocument, BillingInvoiceItem, BillingInvoiceStatus, BillingOperationResult, BillingPaymentMethod, BillingPaymentMethodResult, BillingStatus, BillingTax, BillingUser } from '../../../types/Billing';
import { DocumentEncoding, DocumentType } from '../../../types/GlobalType';
import AxiosFactory from '../../../utils/AxiosFactory';
@@ -9,6 +9,7 @@ import BillingIntegration from '../BillingIntegration';
import BillingStorage from '../../../storage/mongodb/BillingStorage';
import Constants from '../../../utils/Constants';
import Cypher from '../../../utils/Cypher';
+import { DataResult } from '../../../types/DataResult';
import Decimal from 'decimal.js';
import I18nManager from '../../../utils/I18nManager';
import Logging from '../../../utils/Logging';
@@ -536,6 +537,46 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
return billingOperationResult;
}
+ public async paymentMethodsList(user: User): Promise<DataResult<BillingPaymentMethod>> {
+ // Check Stripe
+ await this.checkConnection();
+ // Check billing data consistency
+ // necessary to check stripe user exists here ?
+ // if we don't verify here stripe answers we don't have payment methods - no error is thrown !!
+ if (!user?.billingData?.customerID || await this._isDeletedInStripe(user?.billingData?.customerID)) {
+ // if (!user?.billingData?.customerID) {
+ throw new BackendError({
+ message: 'User is not known in Stripe',
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME,
+ method: 'paymentMethodsList',
+ action: ServerAction.BILLING_TRANSACTION
+ });
+ }
+
+ const paymentMethodResult: BillingPaymentMethodResult = await this._getPaymentMethodsList(user, user.billingData.customerID);
+ return paymentMethodResult;
+ }
+
+ public async deletePaymentMethod(user: User, paymentMethodId: string): Promise<BillingPaymentMethodResult> {
+ // Check Stripe
+ await this.checkConnection();
+ // Check billing data consistency
+ // necessary to check stripe user exists here ?
+ if (!user?.billingData?.customerID || await this._isDeletedInStripe(user?.billingData?.customerID)) {
+ throw new BackendError({
+ message: 'User is not known in Stripe',
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME,
+ method: 'deletePaymentMethod',
+ action: ServerAction.BILLING_TRANSACTION
+ });
+ }
+
+ const paymentMethodResult: BillingPaymentMethodResult = await this._detachPaymentMethod(paymentMethodId, user.billingData.customerID);
+ return paymentMethodResult;
+ }
+
private async _createSetupIntent(user: User, customerID: string): Promise<BillingOperationResult> {
try {
// Let's create a setupIntent for the stripe customer
@@ -619,6 +660,122 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
}
}
+ private async _getPaymentMethodsList(user: User, customerID: string): Promise<DataResult<BillingPaymentMethod>> {
+ const paymentMethodsList = [] as BillingPaymentMethod[];
+ try {
+ let request;
+ const requestParams : Stripe.PaymentMethodListParams = {
+ limit: StripeBillingIntegration.STRIPE_MAX_LIST,
+ customer: customerID,
+ type: 'card',
+ };
+ const customer = await this.getStripeCustomer(customerID);
+ do {
+ request = await this.stripe.paymentMethods.list(requestParams);
+ for (const paymentMethod of request.data) {
+ paymentMethodsList.push({
+ id: paymentMethod.id,
+ brand: paymentMethod.card.brand,
+ expiringOn: (paymentMethod.card.exp_month).toString().padStart(2, '0') + '/' + paymentMethod.card.exp_year,
+ last4: paymentMethod.card.last4,
+ type: paymentMethod.type,
+ createdOn: new Date((paymentMethod.created) * 1000),
+ isDefault: paymentMethod.id === customer.invoice_settings.default_payment_method
+ });
+ }
+ if (request.has_more) {
+ requestParams.starting_after = paymentMethodsList[paymentMethodsList.length - 1].id;
+ }
+ } while (request.has_more);
+ await Logging.logInfo({
+ tenantID: this.tenantID,
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.BILLING_PAYMENT_METHODS_LIST,
+ module: MODULE_NAME, method: '_getPaymentMethodsList',
+ message: `Payment method has been listed for customer '${customerID}' - (${user.email})`
+ });
+ } catch (e) {
+ // catch stripe errors and send the information back to the client
+ await Logging.logError({
+ tenantID: this.tenantID,
+ action: ServerAction.BILLING_SETUP_PAYMENT_METHOD,
+ actionOnUser: user,
+ module: MODULE_NAME, method: '_getPaymentMethodsList',
+ message: `Stripe operation failed - ${e?.message as string}`
+ });
+ }
+ return {
+ count: paymentMethodsList.length,
+ result: paymentMethodsList
+ };
+ }
+
+ private async _detachPaymentMethod(paymentMethodId: string, customerID: string): Promise<BillingPaymentMethodResult> {
+ const detachedPaymentMethod = [] as BillingPaymentMethod[];
+ try {
+ // Verify payment method to be deleted is not the default one
+ const customer = await this.getStripeCustomer(customerID);
+ if (customer.invoice_settings.default_payment_method === paymentMethodId) {
+ throw new BackendError({
+ message: 'Cannot delete default payment method',
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME,
+ method: '_detachPaymentMethod',
+ action: ServerAction.BILLING_TRANSACTION
+ });
+ }
+ // Detach payment method from the stripe customer
+ const paymentMethod: Stripe.PaymentMethod = await this.stripe.paymentMethods.detach(paymentMethodId);
+ detachedPaymentMethod.push({
+ id: paymentMethod.id,
+ brand: paymentMethod.card.brand,
+ expiringOn: (paymentMethod.card.exp_month).toString().padStart(2, '0') + '/' + paymentMethod.card.exp_year,
+ last4: paymentMethod.card.last4,
+ type: paymentMethod.type,
+ createdOn: new Date((paymentMethod.created) * 1000),
+ isDefault: paymentMethod.id === customer.invoice_settings.default_payment_method
+ });
+ await Logging.logInfo({
+ tenantID: this.tenantID,
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.BILLING_DELETE_PAYMENT_METHOD,
+ module: MODULE_NAME, method: '_detachPaymentMethod',
+ message: `Payment method ${paymentMethodId} has been detached - customer '${customerID}'`
+ });
+ } catch (e) {
+ // catch stripe errors and send the information back to the client
+ await Logging.logError({
+ tenantID: this.tenantID,
+ action: ServerAction.BILLING_SETUP_PAYMENT_METHOD,
+ actionOnUser: customerID,
+ module: MODULE_NAME, method: '_detachPaymentMethod',
+ message: `Stripe operation failed - ${e?.message as string}`
+ });
+ }
+ return {
+ count: detachedPaymentMethod.length,
+ result: detachedPaymentMethod
+ };
+ }
+
+ private async _isDeletedInStripe(customerID: string): Promise<boolean> {
+ try {
+ const customer = await this.stripe.customers.retrieve(
+ customerID
+ );
+ return customer.deleted ? customer.deleted : false;
+ } catch (e) {
+ // catch stripe errors and send the information back to the client
+ await Logging.logError({
+ tenantID: this.tenantID,
+ action: ServerAction.BILLING_SETUP_PAYMENT_METHOD,
+ actionOnUser: customerID,
+ module: MODULE_NAME, method: '_getPaymentMethodsList',
+ message: `Stripe operation failed - ${e?.message as string}`
+ });
+ }
+ }
+
public async startTransaction(transaction: Transaction): Promise<BillingDataTransactionStart> {
// Check Stripe
await this.checkConnection();

MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -191,6 +191,7 @@ class RequestMapper {
[ServerAction.BILLING_TAXES]: BillingService.handleGetBillingTaxes.bind(this),
[ServerAction.BILLING_INVOICES]: BillingService.handleGetInvoices.bind(this),
[ServerAction.BILLING_DOWNLOAD_INVOICE]: BillingService.handleDownloadInvoice.bind(this),
+ [ServerAction.BILLING_PAYMENT_METHODS_LIST]: BillingService.handleBillingGetPaymentMethodsList.bind(this),
[ServerAction.OCPI_ENDPOINTS]: OCPIEndpointService.handleGetOcpiEndpoints.bind(this),
[ServerAction.OCPI_ENDPOINT]: OCPIEndpointService.handleGetOcpiEndpoint.bind(this),
[ServerAction.INTEGRATION_CONNECTIONS]: ConnectionService.handleGetConnections.bind(this),
@@ -253,6 +254,7 @@ class RequestMapper {
[ServerAction.CAR_DELETE]: CarService.handleDeleteCar.bind(this),
[ServerAction.TAG_DELETE]: TagService.handleDeleteTag.bind(this),
[ServerAction.TAGS_DELETE]: TagService.handleDeleteTags.bind(this),
+ [ServerAction.BILLING_DELETE_PAYMENT_METHOD]: BillingService.handleBillingDeletePaymentMethod.bind(this),
});
break;
}

MODIFY
src/server/rest/v1/router/api/BillingRouter.ts
src/server/rest/v1/router/api/BillingRouter.ts
+/* eslint-disable @typescript-eslint/no-misused-promises */
import express, { NextFunction, Request, Response } from 'express';
import BillingService from '../../service/BillingService';
@@ -13,6 +14,7 @@ export default class BillingRouter {
public buildRoutes(): express.Router {
this.buildRouteSetupPaymentMethod();
+ this.buildRoutePaymentMethodsList();
return this.router;
}
@@ -21,4 +23,21 @@ export default class BillingRouter {
await RouterUtils.handleServerAction(BillingService.handleBillingSetupPaymentMethod.bind(this), ServerAction.BILLING_SETUP_PAYMENT_METHOD, req, res, next);
});
}
+
+ // why in other routers we can see ServerRoute.REST_BILLING_PAYMENT_METHODS ????
+ // don't get the difference
+ protected buildRoutePaymentMethodsList(): void {
+ this.router.get(`/${ServerAction.BILLING_PAYMENT_METHODS_LIST}`, async (req: Request, res: Response, next: NextFunction) => {
+ // req.query.ID = req.params.id;
+ await RouterUtils.handleServerAction(BillingService.handleBillingGetPaymentMethodsList.bind(this), ServerAction.BILLING_PAYMENT_METHODS_LIST, req, res, next);
+ });
+ }
+
+ // delete ou post ?
+ protected buildRouteDeletePaymentMethod(): void {
+ this.router.delete(`/${ServerAction.BILLING_DELETE_PAYMENT_METHOD}`, async (req: Request, res: Response, next: NextFunction) => {
+ req.query.ID = req.params.id;
+ await RouterUtils.handleServerAction(BillingService.handleBillingDeletePaymentMethod.bind(this), ServerAction.BILLING_DELETE_PAYMENT_METHOD, req, res, next);
+ });
+ }
}

MODIFY
src/server/rest/v1/service/BillingService.ts
src/server/rest/v1/service/BillingService.ts
import { Action, Entity } from '../../../../types/Authorization';
-import { BillingInvoiceStatus, BillingOperationResult, BillingUserSynchronizeAction } from '../../../../types/Billing';
+import { BillingInvoiceStatus, BillingOperationResult, BillingPaymentMethodResult, BillingUserSynchronizeAction } from '../../../../types/Billing';
import { HTTPAuthError, HTTPError } from '../../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
@@ -532,6 +532,82 @@ export default class BillingService {
res.json(operationResult);
}
+ public static async handleBillingGetPaymentMethodsList(action: ServerAction, req: Request, res: Response): Promise<void> {
+ // Filter
+ const filteredRequest = BillingSecurity.filterPaymentMethodsListRequest(req);
+ // Check if component is active
+ UtilsService.assertComponentIsActiveFromToken(filteredRequest.loggedUser, TenantComponents.BILLING,
+ Action.BILLING_PAYMENT_METHODS_LIST, Entity.BILLING, MODULE_NAME, 'handleBillingGetPaymentMethodsList');
+ if (!Authorizations.canListPaymentMethod(filteredRequest.loggedUser)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: filteredRequest.loggedUser,
+ action: Action.CREATE, entity: Entity.PAYMENT_METHOD,
+ module: MODULE_NAME, method: 'handleBillingGetPaymentMethodsList'
+ });
+ }
+ // Get the billing impl
+ const billingImpl = await BillingFactory.getBillingImpl(filteredRequest.loggedUser.tenantID);
+ if (!billingImpl) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Billing service is not configured',
+ module: MODULE_NAME, method: 'handleBillingGetPaymentMethodsList',
+ action: action,
+ user: filteredRequest.loggedUser
+ });
+ }
+ // Get user
+ const user: User = await UserStorage.getUser(filteredRequest.loggedUser.tenantID, filteredRequest.selectedUserID);
+ UtilsService.assertObjectExists(action, user, `User '${filteredRequest.selectedUserID}' does not exist`,
+ MODULE_NAME, 'handleBillingGetPaymentMethodsList', filteredRequest.loggedUser);
+ // Invoke the billing implementation
+ const paymentMethodResult: BillingPaymentMethodResult = await billingImpl.paymentMethodsList(user);
+ if (paymentMethodResult) {
+ console.log(paymentMethodResult);
+ }
+ res.json(paymentMethodResult);
+ }
+
+ public static async handleBillingDeletePaymentMethod(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Filter
+ const filteredRequest = BillingSecurity.filterDeletePaymentMethodRequest(req);
+ // Check if component is active
+ UtilsService.assertComponentIsActiveFromToken(filteredRequest.loggedUser, TenantComponents.BILLING,
+ Action.BILLING_PAYMENT_METHODS_LIST, Entity.BILLING, MODULE_NAME, 'handleBillingDeletePaymentMethod');
+ if (!Authorizations.canDeletePaymentMethod(filteredRequest.loggedUser)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: filteredRequest.loggedUser,
+ action: Action.CREATE, entity: Entity.PAYMENT_METHOD,
+ module: MODULE_NAME, method: 'handleBillingDeletePaymentMethod'
+ });
+ }
+ // Get the billing impl
+ const billingImpl = await BillingFactory.getBillingImpl(filteredRequest.loggedUser.tenantID);
+ if (!billingImpl) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Billing service is not configured',
+ module: MODULE_NAME, method: 'handleBillingDeletePaymentMethod',
+ action: action,
+ user: filteredRequest.loggedUser
+ });
+ }
+ // Get user
+ const user: User = await UserStorage.getUser(filteredRequest.loggedUser.tenantID, filteredRequest.loggedUser.id);
+ UtilsService.assertObjectExists(action, user, `User '${filteredRequest.loggedUser.id}' does not exist`,
+ MODULE_NAME, 'handleBillingDeletePaymentMethod', filteredRequest.loggedUser);
+ // Invoke the billing implementation
+ const paymentMethodResult: BillingPaymentMethodResult = await billingImpl.deletePaymentMethod(user, filteredRequest.paymentMethodId);
+ if (paymentMethodResult) {
+ console.log(paymentMethodResult);
+ }
+ res.json(Constants.REST_RESPONSE_SUCCESS);
+ }
+
public static async handleDownloadInvoice(action: ServerAction, req: Request, res: Response): Promise<void> {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,

MODIFY
src/server/rest/v1/service/security/BillingSecurity.ts
src/server/rest/v1/service/security/BillingSecurity.ts
-import { HttpBillingInvoiceRequest, HttpBillingWebHookRequest, HttpSetupPaymentMethod } from '../../../../../types/requests/HttpBillingRequest';
+import { HttpBillingInvoiceRequest, HttpBillingWebHookRequest, HttpDeletePaymentMethod, HttpPaymentMethodsList, HttpSetupPaymentMethod } from '../../../../../types/requests/HttpBillingRequest';
import { HttpCreateTransactionInvoiceRequest, HttpForceSynchronizeUserInvoicesRequest, HttpSynchronizeUserRequest } from '../../../../../types/requests/HttpUserRequest';
import HttpByIDRequest from '../../../../../types/requests/HttpByIDRequest';
@@ -74,7 +74,24 @@ export default class BillingSecurity {
return {
user: sanitize(request.user),
currentUserID: sanitize(request.body.userID),
- paymentMethodId: sanitize(request.body.paymentMethodId)
+ paymentMethodId: sanitize(request.body.paymentMethodId),
+ };
+ }
+
+ static filterPaymentMethodsListRequest(request: any): HttpPaymentMethodsList {
+ const filteredRequest: HttpPaymentMethodsList = {
+ loggedUser: sanitize(request.user),
+ selectedUserID: sanitize(request.query.userID)
+ };
+ UtilsSecurity.filterSkipAndLimit(request.query, filteredRequest);
+ return filteredRequest;
+ }
+
+ // should add currentuserid to check canDeletePm() ??
+ static filterDeletePaymentMethodRequest(request: any): HttpDeletePaymentMethod {
+ return {
+ loggedUser: sanitize(request.user),
+ paymentMethodId: sanitize(request.body.paymentMethodId),
};
}
}

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -125,6 +125,8 @@ export enum Action {
SYNCHRONIZE_BILLING_USERS = 'SynchronizeBillingUsers',
SYNCHRONIZE_BILLING_USER = 'SynchronizeBillingUser',
BILLING_SETUP_PAYMENT_METHOD = 'BillingSetupPaymentMethod',
+ BILLING_PAYMENT_METHODS_LIST = 'BillingPaymentMethodsList',
+ BILLING_DELETE_PAYMENT_METHOD = 'BillingDeletePaymentMethod',
BILLING_CHARGE_INVOICE = 'BillingChargeInvoice',
CHECK_CONNECTION = 'CheckConnection',
RETRIEVE_CONSUMPTION = 'RetrieveConsumption',

MODIFY
src/types/Billing.ts
src/types/Billing.ts
import { ActionsResponse, DocumentEncoding, DocumentType } from './GlobalType';
+import { Timestamp } from 'mongodb';
import User from './User';
export interface BillingTransactionData {
@@ -117,3 +118,18 @@ export interface BillingOperationResult {
};
internalData?: unknown; // Object returned by the concrete implementation - e.g.: STRIPE
}
+
+export interface BillingPaymentMethod {
+ id: string;
+ brand: string;
+ expiringOn: string;
+ last4: string;
+ type: string;
+ createdOn: Date;
+ isDefault: boolean;
+}
+
+export interface BillingPaymentMethodResult {
+ result: BillingPaymentMethod[];
+ count: number;
+}

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -379,6 +379,8 @@ export enum ServerAction {
BILLING_CREATE_TRANSACTION_INVOICE = 'BillingCreateTransactionInvoice',
BILLING_NEW_INVOICE = 'BillingNewInvoice',
BILLING_SETUP_PAYMENT_METHOD = 'BillingSetupPaymentMethod',
+ BILLING_PAYMENT_METHODS_LIST = 'BillingPaymentMethodsList',
+ BILLING_DELETE_PAYMENT_METHOD = 'BillingDeletePaymentMethod',
BILLING_CHARGE_INVOICE = 'BillingChargeInvoice',
BILLING_WEB_HOOK = 'BillingWebHook',
@@ -449,5 +451,7 @@ export enum ServerRoute {
REST_PING = 'ping',
- REST_TENANTS = 'tenants'
+ REST_TENANTS = 'tenants',
+
+ REST_BILLING_PAYMENT_METHODS = 'billing/paymentmethods'
}

MODIFY
src/types/requests/HttpBillingRequest.ts
src/types/requests/HttpBillingRequest.ts
@@ -23,3 +23,13 @@ export interface HttpSetupPaymentMethod {
currentUserID: string;
paymentMethodId?: string;
}
+
+export interface HttpPaymentMethodsList {
+ loggedUser: UserToken;
+ selectedUserID: string;
+}
+
+export interface HttpDeletePaymentMethod {
+ loggedUser: UserToken;
+ paymentMethodId: string;
+}

added list of payment methods
added deletion
:construction: check in the back user can not delete pm he doesn't own ?
:construction: adapt authorizations