diff --git a/README.md b/README.md index f094530..19f93b4 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/examples/connect/serviceConfig.ts b/examples/connect/serviceConfig.ts new file mode 100644 index 0000000..ca22d4a --- /dev/null +++ b/examples/connect/serviceConfig.ts @@ -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); diff --git a/src/api/api.ts b/src/api/api.ts index 0fc0e79..fbe4f20 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -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'; diff --git a/src/connect/PayNLProvider.ts b/src/connect/PayNLProvider.ts index 4680bd1..5d11969 100644 --- a/src/connect/PayNLProvider.ts +++ b/src/connect/PayNLProvider.ts @@ -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; diff --git a/src/connect/createPayNLClient.ts b/src/connect/createPayNLClient.ts index 0d8cf3d..82dd6ef 100644 --- a/src/connect/createPayNLClient.ts +++ b/src/connect/createPayNLClient.ts @@ -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); @@ -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, diff --git a/src/connect/index.ts b/src/connect/index.ts index 6a468dc..365f240 100644 --- a/src/connect/index.ts +++ b/src/connect/index.ts @@ -1,5 +1,6 @@ export * from './createPayNLClient'; export * from './ApiClient'; export * from './PayNLProvider'; +export * from './service'; export * from './order'; export * from './ApiError'; diff --git a/src/connect/service/ServiceApi.ts b/src/connect/service/ServiceApi.ts new file mode 100644 index 0000000..03c3f1c --- /dev/null +++ b/src/connect/service/ServiceApi.ts @@ -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 { + const response = await this.apiClient.request( + new ConnectApiRequest(`v2/services/config?serviceId=${serviceId}`), + ); + return await response.body(); + } +} diff --git a/src/connect/service/ServiceConfig.ts b/src/connect/service/ServiceConfig.ts new file mode 100644 index 0000000..9bfc6e3 --- /dev/null +++ b/src/connect/service/ServiceConfig.ts @@ -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[]; + }; + }; +}; diff --git a/src/connect/service/index.ts b/src/connect/service/index.ts new file mode 100644 index 0000000..0322c57 --- /dev/null +++ b/src/connect/service/index.ts @@ -0,0 +1 @@ +export * from './ServiceConfig'; diff --git a/tests/connect/service/ServiceApi.test.ts b/tests/connect/service/ServiceApi.test.ts new file mode 100644 index 0000000..3f8bf71 --- /dev/null +++ b/tests/connect/service/ServiceApi.test.ts @@ -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: {}, + }); + }); +}); diff --git a/tests/connect/support/fakeConfig.ts b/tests/connect/support/fakeConfig.ts new file mode 100644 index 0000000..5a552b0 --- /dev/null +++ b/tests/connect/support/fakeConfig.ts @@ -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', + }, + ], +};