Skip to content

Commit

Permalink
refactor: update models and add example
Browse files Browse the repository at this point in the history
  • Loading branch information
Blackfaded committed Jul 26, 2024
1 parent e55dcb6 commit 36f0e3b
Show file tree
Hide file tree
Showing 106 changed files with 698 additions and 427 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ Welcome to the Node SDK for the PayOne PCP platform! This repository contains a

### Run the example app


```sh
cd example-app
yarn
API_KEY=api_key API_SECRET=api_secret MERCHANT_ID=123 COMMERCE_CASE_ID=234 CHECKOUT_ID=345 yarn dev
```

## Contributing

Expand Down
17 changes: 17 additions & 0 deletions example-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "example-app",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "index.js",
"scripts": {
"dev": "ts-node-esm ./src/index.ts"
},
"author": "",
"license": "MIT",
"devDependencies": {
"ts-node": "10.9.2",
"typescript": "5.5.4",
"pcp-server-nodejs-sdk": "0.0.1"
}
}
52 changes: 52 additions & 0 deletions example-app/src/examples/CommerceCaseApiExample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
CommerceCaseApiClient,
CommunicatorConfiguration,
CreateCommerceCaseRequest,
Customer,
GetCommerceCasesQuery,
} from 'pcp-server-nodejs-sdk';

export class CommerceCaseApiExample {
client: CommerceCaseApiClient;
merchantId = process.env.MERCHANT_ID!;
commerceCaseId = process.env.COMMERCE_CASE_ID!;

constructor(config: CommunicatorConfiguration) {
this.client = new CommerceCaseApiClient(config);
}

async runPostOne() {
const payload = new CreateCommerceCaseRequest();
const res = await this.client.createCommerceCaseRequest(this.merchantId, payload);
console.log(res);
}

async runGetAll() {
const query = new GetCommerceCasesQuery();
query.setOffset(2);
query.setSize(1);
const res = await this.client.getCommerceCasesRequest(this.merchantId, query);
console.log(res);
}
async runGetOne() {
const res = await this.client.getCommerceCaseRequest(this.merchantId, this.commerceCaseId);
console.log(res);
}
async runUpdateOne() {
const getOneResponse = await this.client.getCommerceCaseRequest(this.merchantId, this.commerceCaseId);

if (!getOneResponse.customer) {
throw new Error('Customer not found');
}
const modifiedCustomer: Customer = {
...getOneResponse.customer,
billingAddress: {
...getOneResponse.customer.billingAddress,
city: 'New York',
},
};

const res = await this.client.updateCommerceCaseRequest(this.merchantId, this.commerceCaseId, modifiedCustomer);
console.log(res);
}
}
31 changes: 31 additions & 0 deletions example-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { CommunicatorConfiguration } from 'pcp-server-nodejs-sdk';
import { CommerceCaseApiExample } from './examples/CommerceCaseApiExample';

const run = async () => {
const apiKey = process.env.API_KEY;
const apiSecret = process.env.API_SECRET;
const merchantId = process.env.MERCHANT_ID;
const commerceCaseId = process.env.COMMERCE_CASE_ID;
const checkoutId = process.env.CHECKOUT_ID;
if (!apiKey || !apiSecret || !merchantId || !commerceCaseId || !checkoutId) {
console.error(
'Please provide API_KEY, API_SECRET, MERCHANT_ID, COMMERCE_CASE_ID and CHECKOUT_ID as environment variables',
);
process.exit(1);
}

const communicatorConfiguration = new CommunicatorConfiguration(
apiKey,
apiSecret,
'https://preprod.commerce-api.payone.com',
);

const commerceCaseApiClientExample = new CommerceCaseApiExample(communicatorConfiguration);

commerceCaseApiClientExample.runPostOne();
// commerceCaseApiClientExample.runGetAll();
// commerceCaseApiClientExample.runGetOne();
// commerceCaseApiClientExample.runUpdateOne();
};

run();
16 changes: 16 additions & 0 deletions example-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES6",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist" /* Skip type checking all .d.ts files. */
},
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
}
}
162 changes: 162 additions & 0 deletions example-app/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"

"@jridgewell/resolve-uri@^3.0.3":
version "3.1.2"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==

"@jridgewell/sourcemap-codec@^1.4.10":
version "1.5.0"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==

"@jridgewell/[email protected]":
version "0.3.9"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"

"@tsconfig/node10@^1.0.7":
version "1.0.11"
resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz"
integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==

"@tsconfig/node12@^1.0.7":
version "1.0.11"
resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==

"@tsconfig/node14@^1.0.0":
version "1.0.3"
resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==

"@tsconfig/node16@^1.0.2":
version "1.0.4"
resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==

"@types/node@*":
version "20.14.12"
resolved "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz"
integrity sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==
dependencies:
undici-types "~5.26.4"

acorn-walk@^8.1.1:
version "8.3.3"
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz"
integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
dependencies:
acorn "^8.11.0"

acorn@^8.11.0, acorn@^8.4.1:
version "8.12.1"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz"
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==

arg@^4.1.0:
version "4.1.3"
resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==

create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==

data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==

diff@^4.0.1:
version "4.0.2"
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==

fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz"
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"

formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"

make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==

node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==

"pcp-server-nodejs-sdk@file:..":
version "0.0.1"
resolved "file:.."
dependencies:
node-fetch "3.3.2"

[email protected]:
version "10.9.2"
resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz"
integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"

typescript@>=2.7, [email protected]:
version "5.5.4"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz"
integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==

undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==

web-streams-polyfill@^3.0.3:
version "3.3.3"
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz"
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==

[email protected]:
version "3.1.1"
resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "pcp-server-nodejs-sdk",
"version": "0.0.1",
"description": "",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
Expand All @@ -19,12 +20,12 @@
"@types/node": "20.14.12",
"eslint": "9.7.0",
"globals": "15.8.0",
"prettier": "^3.3.3",
"prettier": "3.3.3",
"ts-node": "10.9.2",
"typescript": "5.5.4",
"typescript-eslint": "7.17.0"
},
"dependencies": {
"node-fetch": "^3.3.2"
"node-fetch": "3.3.2"
}
}
16 changes: 9 additions & 7 deletions src/RequestHeaderGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import * as crypto from 'crypto';
import { Headers, RequestInit } from 'node-fetch';
import { CommunicatorConfiguration } from './CommunicatorConfiguration';
import { ServerMetaInfo } from './utils/ServerMetaInfo';
import { URL } from 'url';

import { CommunicatorConfiguration } from './CommunicatorConfiguration.js';
import { ServerMetaInfo } from './utils/ServerMetaInfo.js';

export class RequestHeaderGenerator {
public static readonly SERVER_META_INFO_HEADER_NAME = 'X-GCS-ServerMetaInfo';
public static readonly CLIENT_META_INFO_HEADER_NAME = 'X-GCS-ClientMetaInfo';
private static readonly ALGORITHM = 'HmacSHA256';
private static readonly ALGORITHM = 'sha256';
private static readonly WHITESPACE_REGEX = /\r?\n[h]*/g;
private readonly DATE_HEADER_NAME = 'Date';
private readonly AUTHORIZATION_HEADER_NAME = 'Authorization';

private readonly config: CommunicatorConfiguration;
private readonly hmac: crypto.Hmac;

constructor(config: CommunicatorConfiguration) {
this.config = config;
this.hmac = crypto.createHmac(RequestHeaderGenerator.ALGORITHM, config.getApiSecret());
}

public generateAdditionalRequestHeaders(url: string, request: RequestInit): RequestInit {
Expand Down Expand Up @@ -46,7 +46,7 @@ export class RequestHeaderGenerator {
let stringToSign = `${request.method}\n`;
// 2. Content-Type
if (headers.has('Content-Type')) {
stringToSign += `${headers.get('Content-Type')}\n`;
stringToSign += `${headers.get('Content-Type')}`;
}
stringToSign += '\n';
// 3. Date
Expand All @@ -65,12 +65,14 @@ export class RequestHeaderGenerator {
stringToSign += `${urlInternal.search}`;
}
stringToSign += '\n';
console.log({ stringToSign });
const signature = this.sign(stringToSign);
return `GCS v1HMAC:${this.config.getApiKey()}:${signature}`;
}

private sign(target: string): string {
const hash = this.hmac.update(target).digest();
const hmac = crypto.createHmac(RequestHeaderGenerator.ALGORITHM, this.config.getApiSecret());
const hash = hmac.update(target).digest();
return hash.toString('base64');
}

Expand Down
13 changes: 7 additions & 6 deletions src/endpoints/BaseApiClient.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import fetch, { RequestInit, Response } from 'node-fetch';
import { CommunicatorConfiguration } from '../CommunicatorConfiguration';
import { RequestHeaderGenerator } from '../RequestHeaderGenerator';
import { ApiResponseRetrievalException } from '../errors';
import { ErrorResponse } from '../models';
import { CommunicatorConfiguration } from '../CommunicatorConfiguration.js';
import { RequestHeaderGenerator } from '../RequestHeaderGenerator.js';
import { ApiResponseRetrievalException } from '../errors/index.js';
import { ErrorResponse } from '../models/index.js';

export class BaseApiClient {
private readonly JSON_PARSE_ERROR = 'Expected valid JSON response, but failed to parse';
Expand All @@ -24,9 +24,10 @@ export class BaseApiClient {

protected async makeApiCall<T>(url: string, requestInit: RequestInit): Promise<T> {
requestInit = this.requestHeaderGenerator.generateAdditionalRequestHeaders(url, requestInit);

console.log(requestInit.headers);
const response = await fetch(url, requestInit);
await this.handleError(response);
console.log(response);
await this.handleError(response.clone());
return response.json() as Promise<T>;
}

Expand Down
Loading

0 comments on commit 36f0e3b

Please sign in to comment.