Skip to content

Commit

Permalink
feat: portokasse top up implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
schaechinger committed Aug 10, 2021
1 parent 5863d65 commit e355726
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 24 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,31 @@ from the Portokasse service.

### Top Up Account

Top up is the main method of the Portokasse service. There are two different
payment methods: PayPal and Giropay. Giropay expects a BIC string to be also
passed to the method. Both methods will result in a `redirect` link that should
be called by the user to finish the top up request.

**Info:** The minimum amount to top up is EUR 10,00 which can be defined as an
`Amount` object or a raw number in Euro Cents.

**PayPal top up**

```typescript
const amount = { value: 10, currency: 'EUR' }; // or: const amount = 1000;
const payment = await internetmarke.topUp(amount, PaymentMethod.PayPal);
// payment: { code: 'OK', redirect: 'https://paypal.com/...' }
```

**GiroPay top up**

```typescript
const amount = { value: 10, currency: 'EUR' }; // or: const amount = 1000;
const bic = 'HOLVDEB1XXX';
const payment = await internetmarke.topUp(amount, PaymentMethod.GiroPay, bic);
// payment: { code: 'OK', redirect: 'https://giropay.de/...' }
```

## ProdWS (Product Service)

The product list contains all available vouchers that can be ordered. They are
Expand Down
9 changes: 7 additions & 2 deletions src/Internetmarke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Amount, Product } from './prodWs/product';
import { ProductService, ProductServiceOptions, ProdWS } from './prodWs/Service';
import {
PaymentMethod,
PaymentResponse,
Portokasse,
PortokasseService,
PortokasseServiceOptions
Expand Down Expand Up @@ -318,13 +319,17 @@ export class Internetmarke implements OneClickForApp, Portokasse, ProdWS {
return this.portokasseService;
}

public topUp(amount: Amount | number, paymentMethod: PaymentMethod): Promise<Amount | false> {
public topUp(
amount: Amount | number,
paymentMethod: PaymentMethod,
bic?: string
): Promise<PaymentResponse> {
this.checkServiceInit(
this.portokasseService,
'Cannot get balance before initializing portokasse service'
);

return this.portokasseService.topUp(amount, paymentMethod);
return this.portokasseService.topUp(amount, paymentMethod, bic);
}

protected init(): void {
Expand Down
3 changes: 3 additions & 0 deletions src/portokasse/Error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { InternetmarkeError } from '../Error';

export class PortokasseError extends InternetmarkeError {}
67 changes: 52 additions & 15 deletions src/portokasse/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { inject, injectable } from 'inversify';
import { CookieJar } from 'tough-cookie';
import { TYPES } from '../di/types';
import { UserError } from '../Error';
import { PortokasseError } from './Error';
import { Amount } from '../prodWs/product';
import { RestService } from '../services/Rest';
import { User, UserCredentials, UserInfo } from '../User';
Expand All @@ -13,15 +14,25 @@ export enum PaymentMethod {
Paypal = 'PAYPAL'
}

export interface PaymentResponse {
code: string; // 'OK'
redirect: string; // paypal url
}

export interface PortokasseServiceOptions {
user: UserCredentials;
}

export interface Portokasse {
topUp(amount: Amount | number, paymentMethod: PaymentMethod): Promise<Amount | false>;
getUserInfo(): Promise<UserInfo>;
topUp(
amount: Amount | number,
paymentMethod: PaymentMethod,
bic?: string
): Promise<PaymentResponse>;
}

export const BASE_URL = 'https://portokasse.deutschepost.de/portokasse';
const BASE_URL = 'https://portokasse.deutschepost.de/portokasse';

@injectable()
export class PortokasseService extends RestService implements Portokasse {
Expand All @@ -45,7 +56,7 @@ export class PortokasseService extends RestService implements Portokasse {
}

public isInitialized(): boolean {
return !!this.cookieJar;
return this.user.isAuthenticated();
}

public async getUserInfo(): Promise<UserInfo> {
Expand All @@ -61,10 +72,20 @@ export class PortokasseService extends RestService implements Portokasse {
}

public async topUp(
_amount: Amount | number,
_paymentMethod: PaymentMethod
): Promise<Amount | false> {
return false;
amount: Amount | number,
paymentMethod: PaymentMethod,
bic?: string
): Promise<PaymentResponse> {
const data: any = {
amount: 'number' === typeof amount ? amount : (amount as Amount).value * 100,
paymentMethod
};

if (PaymentMethod.GiroPay === paymentMethod) {
data.bic = bic;
}

return this.request('POST', '/api/v1/payments', data);
}

private async login(): Promise<boolean> {
Expand All @@ -84,19 +105,30 @@ export class PortokasseService extends RestService implements Portokasse {
withCredentials: true
};

const isLogin = '/login' === path;

if (data) {
const encodedData: string[] = [];
for (let prop in data) {
encodedData.push(`${prop}=${encodeURIComponent(data[prop])}`);
if (isLogin) {
const encodedData: string[] = [];
for (let prop in data) {
encodedData.push(`${prop}=${encodeURIComponent(data[prop])}`);
}

options.data = encodedData.join('&');
} else {
options.data = data;
}

options.data = encodedData.join('&');
}
if (this.csrf) {
options.headers = {
'X-CSRF-TOKEN': this.csrf,
'Content-Type': 'application/x-www-form-urlencoded'
'X-CSRF-TOKEN': this.csrf
};

if (data) {
options.headers['Content-Type'] = isLogin
? 'application/x-www-form-urlencoded'
: 'application/json';
}
}

try {
Expand All @@ -112,7 +144,12 @@ export class PortokasseService extends RestService implements Portokasse {

return res.data;
} catch (e) {
return e.response?.data || null;
const error = new PortokasseError(
`Error from Portokasse: ${e.response?.data.code || 'Unknown'}`
);
(error as any).response = e.response || e.message;

throw error;
}
}
}
37 changes: 30 additions & 7 deletions test/portokasse/Service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { UserError } from '../../src/Error';
import { PaymentMethod, PortokasseService } from '../../src/portokasse/Service';
import { userCredentials } from '../1c4a/helper';
import { User } from '../../src/User';
import { PortokasseError } from '../../src/portokasse/Error';

describe('Portokasse Service', () => {
let service: PortokasseService;
Expand Down Expand Up @@ -38,7 +39,7 @@ describe('Portokasse Service', () => {
});

it('should init with minimal options', async () => {
expect(service.init({ user: userCredentials })).to.eventually.be.fulfilled;
await service.init({ user: userCredentials });
expect(service.isInitialized()).to.be.true;
});
});
Expand All @@ -53,9 +54,7 @@ describe('Portokasse Service', () => {
}
});

const info = await service.getUserInfo();

expect(info.isAuthenticated).to.be.false;
expect(service.getUserInfo()).to.eventually.be.rejectedWith(PortokasseError);
});

it('should retrieve user balance', async () => {
Expand All @@ -77,10 +76,34 @@ describe('Portokasse Service', () => {
});

describe('topUp', () => {
it('should add tests once topup is implemented', async () => {
const res = await service.topUp(1000, PaymentMethod.GiroPay);
it('should top up with GiroPay', async () => {
moxios.stubOnce('post', /\/payments$/, {
status: 200,
headers: {},
response: {
code: 'OK',
redirect: 'http://localhost'
}
});

const res = await service.topUp(1000, PaymentMethod.GiroPay, 'XXXXDEXXXX');

expect(res).to.exist;
});

it('should top up with Paypal', async () => {
moxios.stubOnce('post', /\/payments$/, {
status: 200,
headers: {},
response: {
code: 'OK',
redirect: 'http://localhost'
}
});

const res = await service.topUp(1000, PaymentMethod.Paypal);

expect(res).to.be.false;
expect(res).to.exist;
});
});
});

0 comments on commit e355726

Please sign in to comment.