@payyo/ptp-js 1.0.0-beta.1 → 2.0.0

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.
@@ -0,0 +1,95 @@
1
+ export declare function createForm(config: FormConfig): Form;
2
+
3
+ export declare type Field = {
4
+ iframe: HTMLIFrameElement;
5
+ name: string;
6
+ type: FieldType;
7
+ iFrameName: string;
8
+ container: HTMLElement;
9
+ title: string;
10
+ placeholder: string;
11
+ inputType: string;
12
+ isReady: boolean;
13
+ };
14
+
15
+ export declare type FieldConfig = {
16
+ fieldId: string;
17
+ fieldName: string;
18
+ fieldType: FieldType;
19
+ formId: string;
20
+ fieldNames: string[];
21
+ title: string;
22
+ placeholder: string;
23
+ inputType: string;
24
+ endpoint: string;
25
+ mode: Mode;
26
+ debug: boolean;
27
+ };
28
+
29
+ export declare type FieldEvent = {
30
+ fieldName: string;
31
+ params: any;
32
+ };
33
+
34
+ export declare type FieldParams = {
35
+ type: FieldType;
36
+ container: HTMLElement | string;
37
+ placeholder: string;
38
+ title: string;
39
+ inputType?: string;
40
+ };
41
+
42
+ export declare enum FieldType {
43
+ CardNumber = "cardNumber",
44
+ SecurityCode = "securityCode"
45
+ }
46
+
47
+ export declare type Form = {
48
+ init: (fieldsMap: Record<string, FieldParams>) => void;
49
+ onChange: (handler: (event: FieldEvent) => void) => void;
50
+ onReady: (handler: (event: FieldEvent) => void) => void;
51
+ onFocus: (handler: (event: FieldEvent) => void) => void;
52
+ onBlur: (handler: (event: FieldEvent) => void) => void;
53
+ focus: (field: string) => void;
54
+ validate: () => Promise<any>;
55
+ setStyle: (fieldName: string, selector: string, cssRules: Record<string, any>) => void;
56
+ submit: (params?: any) => Promise<any>;
57
+ destroy: () => void;
58
+ };
59
+
60
+ export declare type FormConfig = {
61
+ mode: Mode | string;
62
+ debug: boolean;
63
+ /** is used in `Mode.Test` mode only */
64
+ fieldUrl?: string;
65
+ };
66
+
67
+ export declare type Message = {
68
+ params: any;
69
+ type: string;
70
+ formId: string;
71
+ messageId: string | null;
72
+ response?: MessageResponse;
73
+ };
74
+
75
+ export declare type MessageResponse = {
76
+ success: boolean;
77
+ result?: null;
78
+ error?: string | null;
79
+ fields?: Record<string, boolean>;
80
+ error_details?: string;
81
+ };
82
+
83
+ export declare enum Mode {
84
+ Test = "test",
85
+ Live = "live"
86
+ }
87
+
88
+ export declare type ReplyCallback = (type: string, event: MessageResponse) => void;
89
+
90
+ export declare type SetStyleEvent = {
91
+ selector: string;
92
+ rules: string[];
93
+ };
94
+
95
+ export { }
@@ -3,12 +3,17 @@ var Mode = /* @__PURE__ */ ((Mode2) => {
3
3
  Mode2["Live"] = "live";
4
4
  return Mode2;
5
5
  })(Mode || {});
6
- function form(config) {
6
+ var FieldType = /* @__PURE__ */ ((FieldType2) => {
7
+ FieldType2["CardNumber"] = "cardNumber";
8
+ FieldType2["SecurityCode"] = "securityCode";
9
+ return FieldType2;
10
+ })(FieldType || {});
11
+ function createForm(config) {
7
12
  const fields = {};
8
13
  const fieldNames = [];
9
14
  const callbacks = {};
10
15
  const formId = generateRandomId();
11
- let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/1.0.0-beta.1/field.html";
16
+ let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/2.0.0/field.html";
12
17
  const EVENT_ON_CHANGE = "change";
13
18
  const EVENT_ON_READY = "ready";
14
19
  const EVENT_ON_FOCUS = "focus";
@@ -32,7 +37,8 @@ function form(config) {
32
37
  // @ts-ignore
33
38
  src: `${fieldUrl}?${new URLSearchParams({
34
39
  form_id: formId,
35
- field_name: field.fieldName,
40
+ field_name: field.name,
41
+ field_type: field.type,
36
42
  field_names: fieldNames,
37
43
  main_field_name: mainFieldName || "",
38
44
  title: field.title,
@@ -67,7 +73,8 @@ function form(config) {
67
73
  message.messageId = messageId;
68
74
  log("send message", message);
69
75
  if (mainFieldName) {
70
- window.frames[fields[mainFieldName].iFrameName].postMessage(message, "*");
76
+ const mainIFrameName = fields[mainFieldName].iFrameName;
77
+ window.frames[mainIFrameName].postMessage(message, "*");
71
78
  }
72
79
  });
73
80
  }
@@ -94,19 +101,22 @@ function form(config) {
94
101
  }
95
102
  }
96
103
  }
97
- const form2 = {
104
+ const form = {
98
105
  init: function(fieldsMap) {
99
106
  for (let fieldName in fieldsMap) {
100
107
  if (!fieldsMap.hasOwnProperty(fieldName)) {
101
108
  return;
102
109
  }
103
110
  let field = fieldsMap[fieldName];
104
- let fieldContainer = field.container;
105
- if (typeof fieldContainer === "string") {
106
- fieldContainer = document.getElementById(fieldContainer);
111
+ let fieldContainer = null;
112
+ if (typeof field.container === "string") {
113
+ fieldContainer = document.getElementById(field.container);
114
+ } else {
115
+ fieldContainer = field.container;
107
116
  }
108
117
  fields[fieldName] = {
109
- fieldName,
118
+ name: fieldName,
119
+ type: field.type,
110
120
  iFrameName: "ptp-field-iframe-" + formId + "-" + fieldName,
111
121
  container: fieldContainer,
112
122
  title: field.title || "",
@@ -220,16 +230,18 @@ function form(config) {
220
230
  const messageId = event.messageId, callback = callbacks[messageId || ""] ?? void 0;
221
231
  if (messageId && callback !== void 0) {
222
232
  delete callbacks[messageId];
223
- callback.apply(form2, [event.response]);
233
+ callback.apply(form, [event.response]);
224
234
  } else {
225
235
  const handler = handlers2[event.type];
226
236
  if (handler) {
227
- handler.apply(form2, [event.response]);
237
+ handler.apply(form, [event.response]);
228
238
  }
229
239
  }
230
240
  }
231
- return form2;
241
+ return form;
232
242
  }
233
243
  export {
234
- form as default
244
+ FieldType,
245
+ Mode,
246
+ createForm
235
247
  };
package/dist/index.js ADDED
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ var Mode = /* @__PURE__ */ ((Mode2) => {
4
+ Mode2["Test"] = "test";
5
+ Mode2["Live"] = "live";
6
+ return Mode2;
7
+ })(Mode || {});
8
+ var FieldType = /* @__PURE__ */ ((FieldType2) => {
9
+ FieldType2["CardNumber"] = "cardNumber";
10
+ FieldType2["SecurityCode"] = "securityCode";
11
+ return FieldType2;
12
+ })(FieldType || {});
13
+ function createForm(config) {
14
+ const fields = {};
15
+ const fieldNames = [];
16
+ const callbacks = {};
17
+ const formId = generateRandomId();
18
+ let fieldUrl = config.fieldUrl && config.mode === Mode.Test ? config.fieldUrl : "https://cdn.ptp.payyo.ch/ptp-js/2.0.0/field.html";
19
+ const EVENT_ON_CHANGE = "change";
20
+ const EVENT_ON_READY = "ready";
21
+ const EVENT_ON_FOCUS = "focus";
22
+ const EVENT_ON_BLUR = "blur";
23
+ let mainFieldName = null;
24
+ function generateRandomId() {
25
+ return Math.random().toString(36).substring(2, 9);
26
+ }
27
+ function log(...args) {
28
+ if (config.debug && window.console) {
29
+ args.unshift("[PTP FORM]");
30
+ console.log.apply(console, args);
31
+ }
32
+ }
33
+ function createIframe(field) {
34
+ const placeholder = field.container, iFrameName = field.iFrameName, attributes = {
35
+ id: iFrameName,
36
+ name: iFrameName,
37
+ title: field.title,
38
+ class: iFrameName,
39
+ // @ts-ignore
40
+ src: `${fieldUrl}?${new URLSearchParams({
41
+ form_id: formId,
42
+ field_name: field.name,
43
+ field_type: field.type,
44
+ field_names: fieldNames,
45
+ main_field_name: mainFieldName || "",
46
+ title: field.title,
47
+ placeholder: field.placeholder,
48
+ input_type: field.inputType,
49
+ mode: config.mode,
50
+ debug: config.debug ? "1" : "0"
51
+ }).toString()}`,
52
+ frameborder: "0",
53
+ scrolling: "no",
54
+ style: "width: 100%; height: 100%; border: none;"
55
+ }, iframe = document.createElement("iframe");
56
+ Object.keys(attributes).forEach(function(key) {
57
+ iframe.setAttribute(key, attributes[key]);
58
+ });
59
+ while (placeholder.lastChild && placeholder.hasChildNodes()) {
60
+ placeholder.removeChild(placeholder.lastChild);
61
+ }
62
+ placeholder.appendChild(iframe);
63
+ field.iframe = iframe;
64
+ }
65
+ function createRemainingIframes() {
66
+ for (let i = 1; i < fieldNames.length; i++) {
67
+ createIframe(fields[fieldNames[i]]);
68
+ }
69
+ }
70
+ function postMessage(type, params = {}) {
71
+ const message = { params, type, formId, messageId: null };
72
+ return new Promise((resolve) => {
73
+ const messageId = `${type}:${generateRandomId()}`;
74
+ callbacks[messageId] = resolve;
75
+ message.messageId = messageId;
76
+ log("send message", message);
77
+ if (mainFieldName) {
78
+ const mainIFrameName = fields[mainFieldName].iFrameName;
79
+ window.frames[mainIFrameName].postMessage(message, "*");
80
+ }
81
+ });
82
+ }
83
+ const handlers = {};
84
+ function emitEvent(...args) {
85
+ const eventName = args.shift();
86
+ const handler = handlers[eventName];
87
+ if (handler) {
88
+ log(`emit event "${eventName}"`, args);
89
+ handler.apply(null, args);
90
+ } else {
91
+ log(`no handler for event "${eventName}"`, args);
92
+ }
93
+ }
94
+ window.addEventListener("message", receiveMessage, false);
95
+ function addOnTouchedEventListener() {
96
+ window.addEventListener("touchend", checkFocus, true);
97
+ }
98
+ function checkFocus() {
99
+ for (let [, field] of Object.entries(fields)) {
100
+ const iframe = window.frames[field.iFrameName];
101
+ if (iframe) {
102
+ iframe.postMessage({ type: "checkFocus" }, "*");
103
+ }
104
+ }
105
+ }
106
+ const form = {
107
+ init: function(fieldsMap) {
108
+ for (let fieldName in fieldsMap) {
109
+ if (!fieldsMap.hasOwnProperty(fieldName)) {
110
+ return;
111
+ }
112
+ let field = fieldsMap[fieldName];
113
+ let fieldContainer = null;
114
+ if (typeof field.container === "string") {
115
+ fieldContainer = document.getElementById(field.container);
116
+ } else {
117
+ fieldContainer = field.container;
118
+ }
119
+ fields[fieldName] = {
120
+ name: fieldName,
121
+ type: field.type,
122
+ iFrameName: "ptp-field-iframe-" + formId + "-" + fieldName,
123
+ container: fieldContainer,
124
+ title: field.title || "",
125
+ placeholder: field.placeholder || "",
126
+ inputType: field.inputType || "text",
127
+ isReady: false
128
+ };
129
+ fieldNames.push(fieldName);
130
+ }
131
+ mainFieldName = fieldNames[0];
132
+ createIframe(fields[mainFieldName]);
133
+ },
134
+ onChange(handler) {
135
+ handlers[EVENT_ON_CHANGE] = handler;
136
+ },
137
+ onReady(handler) {
138
+ handlers[EVENT_ON_READY] = handler;
139
+ },
140
+ onFocus(handler) {
141
+ handlers[EVENT_ON_FOCUS] = handler;
142
+ },
143
+ onBlur(handler) {
144
+ handlers[EVENT_ON_BLUR] = handler;
145
+ },
146
+ validate() {
147
+ return postMessage("validateRequest");
148
+ },
149
+ submit(params) {
150
+ return postMessage("submitRequest", {
151
+ params: params || {},
152
+ browserDetails: {
153
+ browserUserAgent: navigator.userAgent,
154
+ browserJavaEnabled: navigator.javaEnabled(),
155
+ browserLanguage: navigator.language,
156
+ browserColorDepth: screen.colorDepth,
157
+ browserScreenHeight: screen.height,
158
+ browserScreenWidth: screen.width,
159
+ browserTZ: (/* @__PURE__ */ new Date()).getTimezoneOffset()
160
+ }
161
+ }).then((res) => {
162
+ if (res.success) {
163
+ return Promise.resolve(res.result);
164
+ }
165
+ return Promise.reject({
166
+ error: res.error,
167
+ error_details: res.error_details
168
+ });
169
+ });
170
+ },
171
+ focus(field) {
172
+ const f = fields[field] || null;
173
+ if (f) {
174
+ window.frames[f.iFrameName].focus();
175
+ }
176
+ },
177
+ setStyle: function(fieldName, selector, cssRules) {
178
+ if (!fields[fieldName]) {
179
+ return;
180
+ }
181
+ window.frames[fields[fieldName].iFrameName].postMessage({
182
+ type: "setStyle",
183
+ params: {
184
+ selector,
185
+ rules: cssRules
186
+ }
187
+ }, "*");
188
+ },
189
+ destroy: function() {
190
+ var _a;
191
+ window.removeEventListener("message", receiveMessage, false);
192
+ window.addEventListener("touchend", checkFocus, true);
193
+ for (let fieldName in fields) {
194
+ let iframe = document.getElementById(fields[fieldName].iFrameName);
195
+ if (iframe) {
196
+ (_a = iframe.parentNode) == null ? void 0 : _a.removeChild(iframe);
197
+ }
198
+ }
199
+ }
200
+ };
201
+ function receiveMessage(message) {
202
+ const event = message.data;
203
+ if (formId !== event.formId) {
204
+ return;
205
+ }
206
+ log(`message received of type "${event.type}"`, event);
207
+ const handlers2 = {
208
+ fieldReady: (event2) => {
209
+ if (fields[event2.fieldName] === void 0) {
210
+ return;
211
+ }
212
+ fields[event2.fieldName].isReady = true;
213
+ if (event2.fieldName === mainFieldName) {
214
+ createRemainingIframes();
215
+ }
216
+ const areAllReady = Object.keys(fields).map((key) => fields[key].isReady).reduce((a, b) => a && b, true);
217
+ if (areAllReady) {
218
+ addOnTouchedEventListener();
219
+ emitEvent(EVENT_ON_READY);
220
+ }
221
+ },
222
+ fieldChanged: function(event2) {
223
+ emitEvent(EVENT_ON_CHANGE, event2);
224
+ },
225
+ fieldFocused: function(event2) {
226
+ emitEvent(EVENT_ON_FOCUS, event2);
227
+ },
228
+ fieldBlurred: function(event2) {
229
+ emitEvent(EVENT_ON_BLUR, event2);
230
+ }
231
+ };
232
+ const messageId = event.messageId, callback = callbacks[messageId || ""] ?? void 0;
233
+ if (messageId && callback !== void 0) {
234
+ delete callbacks[messageId];
235
+ callback.apply(form, [event.response]);
236
+ } else {
237
+ const handler = handlers2[event.type];
238
+ if (handler) {
239
+ handler.apply(form, [event.response]);
240
+ }
241
+ }
242
+ }
243
+ return form;
244
+ }
245
+ exports.FieldType = FieldType;
246
+ exports.Mode = Mode;
247
+ exports.createForm = createForm;
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@payyo/ptp-js",
3
- "version": "1.0.0-beta.1",
3
+ "version": "2.0.0",
4
4
  "description": "JS library for Payyo Tokenization service (PTP)",
5
5
  "private": false,
6
6
  "author": "developers@payyo.ch",
7
7
  "license": "MIT",
8
- "main": "webpack.config.js",
9
8
  "repository": {
10
9
  "type": "git",
11
10
  "url": "git+ssh://git@bitbucket.org/payyoag/ptp-js.git"
@@ -15,8 +14,15 @@
15
14
  },
16
15
  "homepage": "https://bitbucket.org/payyoag/ptp-js#readme",
17
16
  "type": "module",
17
+ "main": "./dist/index.js",
18
+ "module": "./dist/index.es.js",
19
+ "types": "./dist/index.d.ts",
18
20
  "exports": {
19
- ".": "./dist/form.js"
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.es.js",
24
+ "require": "./dist/index.js"
25
+ }
20
26
  },
21
27
  "scripts": {
22
28
  "dev": "vite",
@@ -25,16 +31,21 @@
25
31
  "build:form": "tsc && MODULE_NAME=form vite build --config vite.config.build.js",
26
32
  "build:dev": "npm run build:dev:field",
27
33
  "build:dev:field": "tsc && MODULE_NAME=field vite build --config vite.config.build.js --mode=development",
28
- "preview": "vite preview"
34
+ "preview": "vite preview",
35
+ "test": "vitest run"
29
36
  },
30
37
  "devDependencies": {
38
+ "@fontsource/open-sans": "^5.2.6",
39
+ "@microsoft/api-extractor": "^7.52.8",
31
40
  "@types/cleave.js": "^1.4.12",
32
41
  "@types/node": "^20.12.7",
33
42
  "cleave.js": "^1.6.0",
34
43
  "sass": "^1.75.0",
35
44
  "typescript": "^5.2.2",
36
- "vite": "^5.2.0",
37
- "vite-plugin-singlefile": "^2.0.1"
45
+ "unplugin-dts": "^1.0.0-beta.0",
46
+ "vite": "^6.3.5",
47
+ "vite-plugin-singlefile": "^2.0.1",
48
+ "vitest": "^3.2.3"
38
49
  },
39
50
  "files": [
40
51
  "src/form",
package/src/form/form.ts CHANGED
@@ -1,7 +1,7 @@
1
- import {Field, FieldEvent, Form, FormConfig, Message, MessageResponse, Mode, SetStyleEvent} from "../types";
1
+ import {Field, FieldEvent, Form, FormConfig, Message, MessageResponse, Mode, SetStyleEvent} from "../types.ts";
2
2
 
3
- export default function (config: FormConfig): Form {
4
- const fields: Record<string, any> = {};
3
+ export function createForm(config: FormConfig): Form {
4
+ const fields: Record<string, Field> = {};
5
5
  const fieldNames: string[] = [];
6
6
  const callbacks: Record<string, Function> = {};
7
7
  const formId = generateRandomId();
@@ -37,7 +37,8 @@ export default function (config: FormConfig): Form {
37
37
  // @ts-ignore
38
38
  src: `${fieldUrl}?${(new URLSearchParams({
39
39
  form_id: formId,
40
- field_name: field.fieldName,
40
+ field_name: field.name,
41
+ field_type: field.type,
41
42
  field_names: fieldNames,
42
43
  main_field_name: mainFieldName || '',
43
44
  title: field.title,
@@ -80,7 +81,9 @@ export default function (config: FormConfig): Form {
80
81
 
81
82
  log('send message', message);
82
83
  if (mainFieldName) {
83
- window.frames[fields[mainFieldName].iFrameName].postMessage(message, '*');
84
+ const mainIFrameName = fields[mainFieldName].iFrameName;
85
+ // @ts-ignore
86
+ window.frames[mainIFrameName].postMessage(message, '*');
84
87
  }
85
88
  });
86
89
  }
@@ -106,6 +109,7 @@ export default function (config: FormConfig): Form {
106
109
 
107
110
  function checkFocus() {
108
111
  for (let [, field] of Object.entries(fields)) {
112
+ // @ts-ignore
109
113
  const iframe = window.frames[field.iFrameName];
110
114
  if (iframe) {
111
115
  iframe.postMessage({type: 'checkFocus'}, '*');
@@ -121,14 +125,17 @@ export default function (config: FormConfig): Form {
121
125
  }
122
126
 
123
127
  let field = fieldsMap[fieldName];
124
- let fieldContainer = field.container;
128
+ let fieldContainer = null;
125
129
 
126
- if (typeof fieldContainer === 'string') {
127
- fieldContainer = document.getElementById(fieldContainer);
130
+ if (typeof field.container === 'string') {
131
+ fieldContainer = document.getElementById(field.container);
132
+ } else {
133
+ fieldContainer = field.container as HTMLElement;
128
134
  }
129
135
 
130
- fields[fieldName] = {
131
- fieldName: fieldName,
136
+ fields[fieldName] = <Field>{
137
+ name: fieldName,
138
+ type: field.type,
132
139
  iFrameName: 'ptp-field-iframe-' + formId + '-' + fieldName,
133
140
  container: fieldContainer,
134
141
  title: field.title || '',
@@ -185,6 +192,7 @@ export default function (config: FormConfig): Form {
185
192
  focus(field) {
186
193
  const f = fields[field] || null;
187
194
  if (f) {
195
+ // @ts-ignore
188
196
  window.frames[f.iFrameName].focus();
189
197
  }
190
198
  },
@@ -193,6 +201,7 @@ export default function (config: FormConfig): Form {
193
201
  return;
194
202
  }
195
203
 
204
+ // @ts-ignore
196
205
  window.frames[fields[fieldName].iFrameName].postMessage({
197
206
  type: 'setStyle',
198
207
  params: {
@@ -267,4 +276,4 @@ export default function (config: FormConfig): Form {
267
276
  }
268
277
 
269
278
  return form;
270
- };
279
+ }
@@ -0,0 +1,2 @@
1
+ export * from '../types.ts';
2
+ export * from './form.ts';