@wdft/micropayments-sdk 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.test.ts CHANGED
@@ -3,64 +3,64 @@ import { getViaMsg } from "./index.js";
3
3
  import { mockFetch } from "./test_utils/mock_fetcher.js";
4
4
 
5
5
  it("getViaMsg", async () => {
6
- const mock = mockFetch();
6
+ const mock = mockFetch();
7
7
 
8
- await new Promise((resolve, reject) => {
9
- mock.next(200, {
10
- click_url: "mock_click_url",
11
- incoming_message_device: "mock_num",
12
- expires_at: Date.now() + 30_000,
13
- commitment_id: "sample_commitment_id",
14
- });
8
+ await new Promise((resolve, reject) => {
9
+ mock.next(200, {
10
+ click_url: "mock_click_url",
11
+ incoming_message_device: "mock_num",
12
+ expires_at: Date.now() + 30_000,
13
+ commitment_id: "sample_commitment_id",
14
+ });
15
15
 
16
- mock.next(200, {
17
- message: "waiting for sms",
18
- status: "waiting",
19
- commitment_id: "sample_commitment_id",
20
- });
16
+ mock.next(200, {
17
+ message: "waiting for sms",
18
+ status: "waiting",
19
+ commitment_id: "sample_commitment_id",
20
+ });
21
21
 
22
- mock.next(200, {
23
- message: "waiting for sms",
24
- status: "waiting",
25
- commitment_id: "sample_commitment_id",
26
- });
22
+ mock.next(200, {
23
+ message: "waiting for sms",
24
+ status: "waiting",
25
+ commitment_id: "sample_commitment_id",
26
+ });
27
27
 
28
- mock.next(200, {
29
- message: "waiting for sms",
30
- status: "resolved",
31
- commitment_id: "sample_commitment_id",
32
- });
28
+ mock.next(200, {
29
+ message: "waiting for sms",
30
+ status: "resolved",
31
+ commitment_id: "sample_commitment_id",
32
+ });
33
33
 
34
- getViaMsg(
35
- {
36
- amount: 10,
37
- reference_id: "ref",
38
- currency: "PLN",
39
- tenant_id: "my-tenant",
40
- endpoint: "http://localhost:8787",
41
- fetcher: mock.fetcher.bind(mock),
42
- retry_ms: 100,
43
- },
44
- (error, payload) => {
45
- if (!payload || error) {
46
- console.log(error);
47
- reject(error);
48
- return;
49
- }
34
+ getViaMsg(
35
+ {
36
+ amount: 10,
37
+ reference_id: "ref",
38
+ currency: "PLN",
39
+ tenant_id: "testing_dev_tenant",
40
+ endpoint: "http://localhost:8787",
41
+ fetcher: mock.fetcher.bind(mock),
42
+ retry_ms: 100,
43
+ },
44
+ (error, payload) => {
45
+ if (!payload || error) {
46
+ console.log(error);
47
+ reject(error);
48
+ return;
49
+ }
50
50
 
51
- const { status } = payload;
51
+ const { status } = payload;
52
52
 
53
- switch (status) {
54
- case "waiting":
55
- case "created":
56
- case "duplicated":
57
- case "expired":
58
- console.log(payload);
59
- break;
60
- case "resolved":
61
- resolve(status);
62
- }
63
- },
64
- );
65
- });
53
+ switch (status) {
54
+ case "waiting":
55
+ case "created":
56
+ case "duplicated":
57
+ case "expired":
58
+ console.log(payload);
59
+ break;
60
+ case "resolved":
61
+ resolve(status);
62
+ }
63
+ },
64
+ );
65
+ });
66
66
  });
package/src/index.ts CHANGED
@@ -4,23 +4,23 @@ import type { Fetcher } from "./service.js";
4
4
  import { Client } from "./client.js";
5
5
 
6
6
  export interface IGetViaMsgInit {
7
- reference_id: string;
8
- amount: number;
9
- currency: "PLN" | "EUR" | "GBP" | "USD" | "CHF" | string;
10
- tenant_id: string;
11
- endpoint?: string;
12
- fetcher?: Fetcher;
13
- retry_ms?: number;
7
+ reference_id: string;
8
+ amount: number;
9
+ currency: "PLN" | "EUR" | "GBP" | "USD" | "CHF" | string;
10
+ tenant_id: string;
11
+ endpoint?: string;
12
+ fetcher?: Fetcher;
13
+ retry_ms?: number;
14
14
  }
15
15
 
16
16
  export function getViaMsg(init: IGetViaMsgInit, callback: ICallback) {
17
- const client = Client.new({
18
- currency: init.currency,
19
- endpoint: init.endpoint ?? "https://api.micropayments.wdft.ovh/",
20
- tenant_id: init.tenant_id,
21
- fetcher: init.fetcher ?? fetch,
22
- retry_ms: init.retry_ms ?? 1000,
23
- });
17
+ const client = Client.new({
18
+ currency: init.currency,
19
+ endpoint: init.endpoint ?? "https://api.micropayments.wdft.ovh/",
20
+ tenant_id: init.tenant_id,
21
+ fetcher: init.fetcher ?? fetch,
22
+ retry_ms: init.retry_ms ?? 1000,
23
+ });
24
24
 
25
- client.create(init.reference_id, init.amount, callback);
25
+ client.create(init.reference_id, init.amount, callback);
26
26
  }
@@ -3,16 +3,16 @@ import { expect } from "vitest";
3
3
  import { mockFetch } from "./test_utils/mock_fetcher.js";
4
4
 
5
5
  it("mocking works", async () => {
6
- const mock = mockFetch();
6
+ const mock = mockFetch();
7
7
 
8
- mock.next(200, {
9
- test: 123,
10
- });
8
+ mock.next(200, {
9
+ test: 123,
10
+ });
11
11
 
12
- const response = await mock.fetcher();
12
+ const response = await mock.fetcher();
13
13
 
14
- expect(response.status).toBe(200);
15
- expect(await response.json()).toMatchObject({
16
- test: 123,
17
- });
14
+ expect(response.status).toBe(200);
15
+ expect(await response.json()).toMatchObject({
16
+ test: 123,
17
+ });
18
18
  });
@@ -4,50 +4,50 @@ import { Service } from "./service.js";
4
4
  import { mockFetch } from "./test_utils/mock_fetcher.js";
5
5
 
6
6
  it("service.createCommitment - 200 - resolved good data", async () => {
7
- const mock = mockFetch();
8
-
9
- const my = new Service({
10
- endpoint: "http://localhost:8787",
11
- tenant_id: "testing-dev-tenant",
12
- fetcher: () => mock.fetcher(),
13
- });
14
-
15
- mock.next(200, {
16
- click_url: "mock_click_url",
17
- incoming_message_device: "mock_num",
18
- expires_at: Date.now() + 30_000,
19
- commitment_id: "sample_commitment_id",
20
- });
21
-
22
- const commitment = await my.createCommitment("sample-content-id", 10, "PLN");
23
-
24
- expect(commitment).toMatchObject({
25
- click_url: "mock_click_url",
26
- incoming_message_device: "mock_num",
27
- commitment_id: "sample_commitment_id",
28
- });
7
+ const mock = mockFetch();
8
+
9
+ const my = new Service({
10
+ endpoint: "http://localhost:8787",
11
+ tenant_id: "testing_dev_tenant",
12
+ fetcher: () => mock.fetcher(),
13
+ });
14
+
15
+ mock.next(200, {
16
+ click_url: "mock_click_url",
17
+ incoming_message_device: "mock_num",
18
+ expires_at: Date.now() + 30_000,
19
+ commitment_id: "sample_commitment_id",
20
+ });
21
+
22
+ const commitment = await my.createCommitment("sample-content-id", 10, "PLN");
23
+
24
+ expect(commitment).toMatchObject({
25
+ click_url: "mock_click_url",
26
+ incoming_message_device: "mock_num",
27
+ commitment_id: "sample_commitment_id",
28
+ });
29
29
  });
30
30
 
31
31
  it("service.checkCommitment - 200 - resolved good data", async () => {
32
- const mock = mockFetch();
33
-
34
- const my = new Service({
35
- endpoint: "http://localhost:8787",
36
- tenant_id: "testing-dev-tenant",
37
- fetcher: () => mock.fetcher(),
38
- });
39
-
40
- mock.next(200, {
41
- message: "waiting for sms",
42
- status: "waiting",
43
- commitment_id: "sample_commitment_id",
44
- });
45
-
46
- const status = await my.checkCommitment("sample_commitment_id");
47
-
48
- expect(status).toMatchObject({
49
- message: "waiting for sms",
50
- status: "waiting",
51
- commitment_id: "sample_commitment_id",
52
- });
32
+ const mock = mockFetch();
33
+
34
+ const my = new Service({
35
+ endpoint: "http://localhost:8787",
36
+ tenant_id: "testing_dev_tenant",
37
+ fetcher: () => mock.fetcher(),
38
+ });
39
+
40
+ mock.next(200, {
41
+ message: "waiting for sms",
42
+ status: "waiting",
43
+ commitment_id: "sample_commitment_id",
44
+ });
45
+
46
+ const status = await my.checkCommitment("sample_commitment_id");
47
+
48
+ expect(status).toMatchObject({
49
+ message: "waiting for sms",
50
+ status: "waiting",
51
+ commitment_id: "sample_commitment_id",
52
+ });
53
53
  });
package/src/service.ts CHANGED
@@ -1,177 +1,182 @@
1
1
  interface CustomResponse {
2
- status: number;
3
- error: boolean;
2
+ status: number;
3
+ error: boolean;
4
4
  }
5
5
 
6
6
  interface SuccessResponse<T> extends CustomResponse {
7
- error: false;
8
- message: null;
9
- data: T;
7
+ error: false;
8
+ message: null;
9
+ data: T;
10
10
  }
11
11
 
12
12
  interface ErrorResponse extends CustomResponse {
13
- error: true;
14
- message: string;
15
- data: null;
13
+ error: true;
14
+ message: string;
15
+ data: null;
16
16
  }
17
17
 
18
18
  interface ICommitment {
19
- click_url: string;
20
- incoming_message_device: string;
21
- expires_at: number;
22
- commitment_id: string;
19
+ click_url: string;
20
+ incoming_message_device: string;
21
+ expires_at: number;
22
+ commitment_id: string;
23
+ }
24
+
25
+ export interface ICommitmentMetadata {
26
+ title: string;
27
+ url: string;
23
28
  }
24
29
 
25
30
  type ResponseDTO<T> = SuccessResponse<T> | ErrorResponse;
26
31
 
27
32
  async function parseResponse<T = unknown>(response: Response): Promise<T> {
28
- if (response.headers.get("Content-Type")?.includes("application/json")) {
29
- return (await response.json()) as T;
30
- }
31
- return (await response.text()) as T;
33
+ if (response.headers.get("Content-Type")?.includes("application/json")) {
34
+ return (await response.json()) as T;
35
+ }
36
+ return (await response.text()) as T;
32
37
  }
33
38
 
34
39
  export type Fetcher = (
35
- input: RequestInfo | URL,
36
- init?: RequestInit,
40
+ input: RequestInfo | URL,
41
+ init?: RequestInit,
37
42
  ) => Promise<Response>;
38
43
 
39
44
  async function request<T = unknown>(
40
- path: string,
41
- req: RequestInit,
42
- fetcher: Fetcher,
45
+ path: string,
46
+ req: RequestInit,
47
+ fetcher: Fetcher,
43
48
  ): Promise<ResponseDTO<T | null>> {
44
- try {
45
- const response = await fetcher(new Request(path, req));
46
- if (response.ok) {
47
- return {
48
- status: response.status,
49
- error: false,
50
- data: (await parseResponse(response)) as T,
51
- } as ResponseDTO<T>;
52
- }
53
- const errorPayload = await parseResponse(response);
54
- return {
55
- status: response.status,
56
- error:
57
- errorPayload &&
58
- typeof errorPayload === "object" &&
59
- "error" in errorPayload
60
- ? errorPayload.error
61
- : true,
62
- message:
63
- errorPayload &&
64
- typeof errorPayload === "object" &&
65
- "message" in errorPayload
66
- ? errorPayload.message
67
- : response.statusText,
68
- data: null,
69
- } as ResponseDTO<T>;
70
- } catch (error) {
71
- return {
72
- status: 500,
73
- error: true,
74
- message: error instanceof Error ? error.message : `${error}`,
75
- data: null,
76
- } as ErrorResponse;
77
- }
49
+ try {
50
+ const response = await fetcher(new Request(path, req));
51
+ if (response.ok) {
52
+ return {
53
+ status: response.status,
54
+ error: false,
55
+ data: (await parseResponse(response)) as T,
56
+ } as ResponseDTO<T>;
57
+ }
58
+ const errorPayload = await parseResponse(response);
59
+ return {
60
+ status: response.status,
61
+ error: errorPayload &&
62
+ typeof errorPayload === "object" &&
63
+ "error" in errorPayload
64
+ ? errorPayload.error
65
+ : true,
66
+ message: errorPayload &&
67
+ typeof errorPayload === "object" &&
68
+ "message" in errorPayload
69
+ ? errorPayload.message
70
+ : response.statusText,
71
+ data: null,
72
+ } as ResponseDTO<T>;
73
+ } catch (error) {
74
+ return {
75
+ status: 500,
76
+ error: true,
77
+ message: error instanceof Error ? error.message : `${error}`,
78
+ data: null,
79
+ } as ErrorResponse;
80
+ }
78
81
  }
79
82
 
80
83
  function buildUrl(endpoint: string, path: string) {
81
- const validEndpoint = endpoint.endsWith("/")
82
- ? endpoint.substring(0, endpoint.length - 1)
83
- : endpoint;
84
- const validPath = path.startsWith("/") ? path : "/".concat(path);
85
- return validEndpoint + validPath;
84
+ const validEndpoint = endpoint.endsWith("/")
85
+ ? endpoint.substring(0, endpoint.length - 1)
86
+ : endpoint;
87
+ const validPath = path.startsWith("/") ? path : "/".concat(path);
88
+ return validEndpoint + validPath;
86
89
  }
87
90
 
88
91
  export interface IServiceOptions {
89
- tenant_id: string;
90
- endpoint: string;
91
- fetcher?: Fetcher;
92
+ tenant_id: string;
93
+ endpoint: string;
94
+ fetcher?: Fetcher;
92
95
  }
93
96
 
94
97
  export interface ICommitmentStatus {
95
- commitment_id: string;
96
- status: string;
97
- message: string;
98
+ commitment_id: string;
99
+ status: string;
100
+ message: string;
98
101
  }
99
102
 
100
103
  export class Service {
101
- private readonly fetcher: Fetcher;
102
-
103
- constructor(private readonly options: IServiceOptions) {
104
- if (!options.fetcher && !globalThis.fetch) {
105
- throw new Error(
106
- 'fetch is not available in environment, pass "fetcher" option with satisfies fetch interface',
107
- );
108
- }
109
-
110
- this.fetcher = this.options.fetcher ?? globalThis.fetch;
111
- }
112
-
113
- async createCommitment(
114
- reference_id: string,
115
- amount: number,
116
- currency: string,
117
- ): Promise<ICommitment> {
118
- const response = await request<ICommitment>(
119
- buildUrl(this.options.endpoint, "/commitment"),
120
- {
121
- method: "POST",
122
- body: JSON.stringify({
123
- tenant_id: this.options.tenant_id,
124
- reference_id,
125
- currency: currency,
126
- amount,
127
- }),
128
- },
129
- this.fetcher,
130
- );
131
-
132
- if (response.error) {
133
- throw new Error(response.message);
134
- }
135
-
136
- if (!response.data) {
137
- throw new Error("Bad response");
138
- }
139
-
140
- return response.data;
141
- }
142
-
143
- async checkCommitment(commitment: string): Promise<ICommitmentStatus> {
144
- const response = await request<ICommitmentStatus>(
145
- buildUrl(this.options.endpoint, `/commitment/${commitment}`),
146
- {
147
- method: "GET",
148
- },
149
- this.fetcher,
150
- );
151
-
152
- switch (response.status) {
153
- case 402:
154
- return {
155
- commitment_id: commitment,
156
- message: response.message || "Payment required - you are over limit",
157
- status: "rejected",
158
- };
159
- case 410:
160
- return {
161
- commitment_id: commitment,
162
- message: response.message || "Expired",
163
- status: "expired",
164
- };
165
- }
166
-
167
- if (response.error) {
168
- throw new Error(response.message);
169
- }
170
-
171
- if (!response.data) {
172
- throw new Error("Bad response");
173
- }
174
-
175
- return response.data;
176
- }
104
+ private readonly fetcher: Fetcher;
105
+
106
+ constructor(private readonly options: IServiceOptions) {
107
+ if (!options.fetcher && !globalThis.fetch) {
108
+ throw new Error(
109
+ 'fetch is not available in environment, pass "fetcher" option with satisfies fetch interface',
110
+ );
111
+ }
112
+
113
+ this.fetcher = this.options.fetcher ?? globalThis.fetch;
114
+ }
115
+
116
+ async createCommitment(
117
+ reference_id: string,
118
+ amount: number,
119
+ currency: string,
120
+ metadata?: ICommitmentMetadata
121
+ ): Promise<ICommitment> {
122
+ const response = await request<ICommitment>(
123
+ buildUrl(this.options.endpoint, "/commitment"),
124
+ {
125
+ method: "POST",
126
+ body: JSON.stringify({
127
+ tenant_id: this.options.tenant_id,
128
+ reference_id,
129
+ currency: currency,
130
+ metadata: metadata ?? {title: "", url: ""},
131
+ amount,
132
+ }),
133
+ },
134
+ this.fetcher,
135
+ );
136
+
137
+ if (response.error) {
138
+ throw new Error(response.message);
139
+ }
140
+
141
+ if (!response.data) {
142
+ throw new Error("Bad response");
143
+ }
144
+
145
+ return response.data;
146
+ }
147
+
148
+ async checkCommitment(commitment: string): Promise<ICommitmentStatus> {
149
+ const response = await request<ICommitmentStatus>(
150
+ buildUrl(this.options.endpoint, `/commitment/${commitment}`),
151
+ {
152
+ method: "GET",
153
+ },
154
+ this.fetcher,
155
+ );
156
+
157
+ switch (response.status) {
158
+ case 402:
159
+ return {
160
+ commitment_id: commitment,
161
+ message: response.message || "Payment required - you are over limit",
162
+ status: "rejected",
163
+ };
164
+ case 410:
165
+ return {
166
+ commitment_id: commitment,
167
+ message: response.message || "Expired",
168
+ status: "expired",
169
+ };
170
+ }
171
+
172
+ if (response.error) {
173
+ throw new Error(response.message);
174
+ }
175
+
176
+ if (!response.data) {
177
+ throw new Error("Bad response");
178
+ }
179
+
180
+ return response.data;
181
+ }
177
182
  }
@@ -1,24 +1,25 @@
1
1
  export const mockFetch = () => ({
2
- __responses: [] as Response[],
3
- next(status: number, body: null | string | object, headers: object = {}) {
4
- this.__responses.push(
5
- new Response(typeof body === "object" ? JSON.stringify(body) : body, {
6
- status,
7
- headers: {
8
- "Content-Type":
9
- typeof body === "object" ? "application/json" : "plain/text",
10
- ...headers,
11
- },
12
- }),
13
- );
14
- },
15
- async fetcher() {
16
- const next = this.__responses.shift();
2
+ __responses: [] as Response[],
3
+ next(status: number, body: null | string | object, headers: object = {}) {
4
+ this.__responses.push(
5
+ new Response(typeof body === "object" ? JSON.stringify(body) : body, {
6
+ status,
7
+ headers: {
8
+ "Content-Type": typeof body === "object"
9
+ ? "application/json"
10
+ : "plain/text",
11
+ ...headers,
12
+ },
13
+ }),
14
+ );
15
+ },
16
+ async fetcher() {
17
+ const next = this.__responses.shift();
17
18
 
18
- if (!next) {
19
- throw new Error("not found mock item");
20
- }
19
+ if (!next) {
20
+ throw new Error("not found mock item");
21
+ }
21
22
 
22
- return next;
23
- },
23
+ return next;
24
+ },
24
25
  });