Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ import { createPayNLClient } from 'paynl-sdk';
const payNL = createPayNLClient({ apiToken: '****************************************', serviceId: 'SL-####-####' });
```

## Service Config

To fetch the configuration of a given service location. This can be used to create your own checkout.

```typescript
const config = await payNL.Service.getConfig('SL-####-####');
```

## Orders

Orders use the new Transaction Gateway Unit API. View the [examples](https://github.com/paynl/nodejs-sdk/tree/master/src/examples/connect) or [online documentation](https://developer.pay.nl/reference/api_create_order-1) for a complete overview of what is possible.
Expand Down
7 changes: 7 additions & 0 deletions examples/connect/serviceConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createPayNLClient } from '../../src';

const payNL = createPayNLClient({ apiToken: 'your-api-token', serviceId: 'SL-1234-5678' });

const config = await payNL.Service.getConfig('SL-1234-5678');

console.log(config);
2 changes: 1 addition & 1 deletion src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable */
// @ts-nocheck

import { Config } from '../index';
import { ServiceConfig } from '../index';
import { Observable } from 'rxjs/Observable';
import * as request from 'request';
import 'rxjs/add/observable/throw';
Expand Down
2 changes: 2 additions & 0 deletions src/connect/PayNLProvider.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { OrderApi } from './order/OrderApi';
import { ApiClientInterface } from './ApiClient';
import * as Paynl from '../index';
import { ServiceApi } from './service/ServiceApi';

export type PayNLProvider = {
Orders: OrderApi;
Client: ApiClientInterface;
Service: ServiceApi;
PaymentMethods: typeof Paynl.Paymentmethods;
Instore: typeof Paynl.Instore;
DirectDebit: typeof Paynl.DirectDebit;
Expand Down
2 changes: 2 additions & 0 deletions src/connect/createPayNLClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Paynl from '../index';
import { ClientOptions, ApiClient } from './ApiClient';
import { PayNLProvider } from './PayNLProvider';
import { OrderApi } from './order/OrderApi';
import { ServiceApi } from './service/ServiceApi';

export function createPayNLClient(options: ClientOptions): PayNLProvider {
const apiClient = new ApiClient(options);
Expand All @@ -10,6 +11,7 @@ export function createPayNLClient(options: ClientOptions): PayNLProvider {
return {
Client: apiClient,
Orders: new OrderApi(apiClient),
Service: new ServiceApi(apiClient),
PaymentMethods: Paynl.Paymentmethods,
Instore: Paynl.Instore,
DirectDebit: Paynl.DirectDebit,
Expand Down
1 change: 1 addition & 0 deletions src/connect/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './createPayNLClient';
export * from './ApiClient';
export * from './PayNLProvider';
export * from './service';
export * from './order';
export * from './ApiError';
18 changes: 18 additions & 0 deletions src/connect/service/ServiceApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiClientInterface } from '../ApiClient';
import { ConnectApiRequest } from '../ConnectApiRequest';
import { ServiceConfig } from './ServiceConfig';

export class ServiceApi {
constructor(private readonly apiClient: ApiClientInterface) {}

/**
* Get the complete configuration of a service location. you can use this to create your own checkout.
* @see https://developer.pay.nl/reference/get_services-config
*/
async getConfig(serviceId: string): Promise<ServiceConfig> {
const response = await this.apiClient.request(
new ConnectApiRequest(`v2/services/config?serviceId=${serviceId}`),
Comment thread
Jeroen-G marked this conversation as resolved.
);
return await response.body<ServiceConfig>();
}
}
104 changes: 104 additions & 0 deletions src/connect/service/ServiceConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
export type ServiceConfig = {
mcc: number;
code: string;
name: string;
_links: {
rel: string;
href: string;
type: string;
}[];
layout: null;
secret: string;
status: string;
address: {
city: string;
code: string;
zipCode: string;
streetName: string;
countryCode: string;
streetNumber: string;
};
tguList: {
ID: number;
share: number;
domain: string;
status: string;
}[];
category: {
code: string;
name: string;
};
merchant: {
code: string;
name: string;
status: string;
};
testMode: boolean;
createdAt: string;
createdBy: string;
deletedAt: null;
deletedBy: null;
tradeName: null;
modifiedAt: string;
modifiedBy: string;
contactEmail: string;
contactPhone: string;
translations: {
name: {
nl_NL: string;
};
};
checkoutTexts: never[];
turnoverGroup: {
code: string;
name: string;
};
encryptionKeys: {
createdAt: string;
expiresAt: string;
publicKey: string;
identifier: string;
}[];
checkoutOptions: {
id: number;
tag: string;
name: string;
image: string;
translations: {
name: {
nl_NL?: string;
en_GB?: string;
};
};
paymentMethods: {
id: number;
name: string;
image: string;
options: {
id: string;
name: string;
image: string;
}[];
settings: null;
maxAmount: number;
minAmount: number;
description: string;
translations: {
name: {
[key: string]: string;
};
description: {
[key: string]: string;
};
};
targetCountries: string[];
}[];
requiredFields: null;
}[];
checkoutSequence: {
default: {
primary: string[];
secondary: string[];
};
};
};
1 change: 1 addition & 0 deletions src/connect/service/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ServiceConfig';
22 changes: 22 additions & 0 deletions tests/connect/service/ServiceApi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ServiceApi } from '../../../src/connect/service/ServiceApi';
import { ApiClientMock } from '../support/ApiClientMock';
import { fakeConfig } from '../support/fakeConfig';

jest.mock('request');

describe('ServiceApi', () => {
it('can fetch service location config', async () => {
const clientMock = new ApiClientMock();
const subject = new ServiceApi(clientMock.getMock());

clientMock.mockResponse(fakeConfig);

const response = await subject.getConfig('SL-1234-5678');

expect(response).toBe(fakeConfig);
expect(clientMock.getRequest()).toEqual({
url: 'https://connect.pay.nl/v2/services/config?serviceId=SL-1234-5678',
options: {},
});
});
});
61 changes: 61 additions & 0 deletions tests/connect/support/fakeConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
export const fakeConfig = {
code: 'SL-1234-5678',
secret: ':secret:',
testMode: false,
name: 'Test',
translations: {
name: {
nl_NL: 'Test',
},
},
status: 'ACTIVE',
merchant: {
code: 'M-1234-5678',
name: 'Test',
status: 'INACTIVE',
},
category: {
code: 'CY-1111-2222',
name: 'Zakelijke diensten (B2B)',
},
mcc: 0,
turnoverGroup: {
code: 'CT-1111-2222',
name: 'Test',
},
layout: null,
tradeName: null,
contactPhone: '+310000000000',
contactEmail: 'info@test.nl',
address: {
code: 'CA-1111-2222',
streetName: 'Straat',
streetNumber: '1',
zipCode: '1234 AB',
city: 'Stad',
countryCode: 'NL',
},
createdAt: '2025-07-23T09:50:20+02:00',
createdBy: 'A-3333-4444',
modifiedAt: '2025-07-23T09:51:55+02:00',
modifiedBy: 'A-3333-4444',
deletedAt: null,
deletedBy: null,
checkoutOptions: [],
checkoutSequence: {
default: {
primary: [],
secondary: [],
},
},
checkoutTexts: [],
encryptionKeys: [],
tguList: [],
_links: [
{
href: '/services/config?serviceId=SL-1234-5678',
rel: 'self',
type: 'GET',
},
],
};