xendit-components-web 0.0.7

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/README.md ADDED
@@ -0,0 +1,309 @@
1
+ # xendit-components-web
2
+
3
+ Xendit Components allows you to accept payments directly on your website using the Xendit Sessions API.
4
+
5
+ This SDK's role is to take a Session (created on your server using the Xendit Sessions API) and render a payment UI, and allow you to customize the UI to match your site's look & feel.
6
+
7
+ ## Installation
8
+
9
+ Install using npm:
10
+
11
+ ```sh
12
+ npm install xendit-components-web --save
13
+ ```
14
+
15
+ Or load it directly from our CDN:
16
+
17
+ ```html
18
+ <script src="https://assets.xendit.co/components/VERSION_NUMBER_HERE/index.umd.js"></script>
19
+ ```
20
+
21
+ Our npm package includes TypeScript types. If you're using the CDN, download type declarations from `https://assets.xendit.co/components/VERSION_NUMBER_HERE/index.d.ts`
22
+
23
+ ## Sessions
24
+
25
+ The Xendit Session API is an abstraction over the Xendit Payments API, representing one transaction (or tokenization). Using one session, a user can make any number
26
+ of attempts to pay, one of which can be successful. A successful payment completes the session.
27
+
28
+ Sessions also abstract away any differences between channels, allowing you to write once, and accept payments from any channel Xendit supports.
29
+
30
+ Two types of sessions are available:
31
+
32
+ - `PAY` sessions collect a payment and optionally save a payment token for later use
33
+ - `SAVE` sessions save a payment token
34
+
35
+ ## Quick Start
36
+
37
+ First, initialize the SDK either with either:
38
+
39
+ - `XenditComponentsTest` for frontend development or unit tests
40
+ - `XenditComponents`, for production, which requires a `sessionClientKey`, which comes from the Session object, which you need to create on your server. Make an endpoint that creates a session for the user's current transaction, return the `components_sdk_key` to the client, and pass it into the constructor.
41
+
42
+ ```typescript
43
+ // For frontend development, use XenditComponentsTest
44
+ const components: XenditComponents = new XenditComponentsTest({});
45
+ // For production or e2e testing, use XenditComponents, passing in the components_sdk_key from the Session object
46
+ // const components: XenditComponents = new XenditComponents({ sessionClientKey });
47
+
48
+ // Create a channel picker component
49
+ const channelPicker: HTMLElement = components.createChannelPickerComponent();
50
+
51
+ // Insert the channel picker into your document
52
+ myCheckoutPage.replaceChildren(channelPicker);
53
+
54
+ // Call submit() when the user clicks your submit button
55
+ mySubmitButton.addEventListener("click", () => {
56
+ components.submit();
57
+ });
58
+
59
+ // Listen to the status of the session
60
+ components.addEventListener("session-complete", () => {
61
+ alert("Payment Success");
62
+ });
63
+ components.addEventListener("session-expired-or-canceled", () => {
64
+ alert("Payment cancelled or expired");
65
+ });
66
+ ```
67
+
68
+ ## Components API
69
+
70
+ ### `XenditComponents` and `XenditComponentsTest`
71
+
72
+ The constructor.
73
+
74
+ For production and e2e testing, you need to pass the `sessionClientKey`, which you can get when you create a Session on your server.
75
+
76
+ For development and unit testing, use `XenditComponentsTest`, which uses mock data and doesn't connect to Xendit servers.
77
+
78
+ ### `createChannelPickerComponent`
79
+
80
+ ```typescript
81
+ const htmlElement = components.createChannelPickerComponent();
82
+ myContainer.replaceChildren(htmlElement);
83
+ ```
84
+
85
+ Creates a UI for the user to select a payment channel and fill any required information.
86
+
87
+ This returns a `HTMLElement`, which you need to insert into your document.
88
+
89
+ This method uses caching, it will always return the same channel picker element.
90
+ Changing the current channel will update the channel picker UI, even if it's unmounted.
91
+ If you don't want this, use `destroyComponent.`
92
+
93
+ ### `getActiveChannels`
94
+
95
+ ```typescript
96
+ const channels = components.getActiveChannels();
97
+ ```
98
+
99
+ Returns the list of channels available in this session.
100
+
101
+ ### `getActiveChannelGroups`
102
+
103
+ ```typescript
104
+ const groups = components.getActiveChannelGroups();
105
+ ```
106
+
107
+ Returns a list of channel groups. This can be used to categorize channels by type, if you want to build
108
+ your own channel selection UI. Each channel has a `uiGroup` property which matches one group's `id` property.
109
+
110
+ ### `createChannelComponent`
111
+
112
+ ```typescript
113
+ const channel = components
114
+ .getActiveChannels()
115
+ .find((channel) => channel.channelCode === "CARDS");
116
+ if (channel) {
117
+ const htmlElement = components.createChannelPickerComponent(channel);
118
+ myContainer.replaceChildren(htmlElement);
119
+ }
120
+ ```
121
+
122
+ Selects a payment channel and creates a UI for the user to fill any required information. You
123
+ need to pass in the channel you want to use, as returned from getActiveChannels.
124
+
125
+ This returns a `HTMLElement`, which you need to insert into your document.
126
+
127
+ This method uses caching, it will always return the same element for the same channel, to preserve the
128
+ values the user enters into any form fields. If you don't want that, use `destroyComponent`.
129
+
130
+ ### `createActionContainerComponent`
131
+
132
+ ```typescript
133
+ components.addEventListener("action-begin", () => {
134
+ const htmlElement = components.createActionContainerComponent();
135
+ myActionContainer.replaceChildren(htmlElement);
136
+ });
137
+ ```
138
+
139
+ Creates a container into which any additional actions (e.g. 3DS, QR Codes) will be rendered.
140
+
141
+ This is optional, if you don't create one, the SDK will create a modal with an action container for you.
142
+ You cannot create an action container during an action (i.e. after the `action-begin` event).
143
+
144
+ This returns a `HTMLElement`, which you need to insert into your document.
145
+
146
+ This method does not use caching.
147
+
148
+ ### `submit`
149
+
150
+ ```typescript
151
+ function onSubmitButtonClick() {
152
+ components.submit();
153
+ }
154
+ ```
155
+
156
+ Begins submission for the active payment channel.
157
+
158
+ Call this from the click event of your submit button.
159
+
160
+ Submission is only available when the session is active, a channel is made current by creating a channel component, any required information is collected, and
161
+ another submission is not in progress. Use the `submission-ready` and `submission-not-ready` events to know when submission is available.
162
+
163
+ This calls the [create payment request](https://docs.xendit.co/apidocs/create-payment-request)
164
+ or [create payment token](https://docs.xendit.co/apidocs/create-payment-token) endpoint depending on the session type. You
165
+ may listen to the corresponding webhooks on your server.
166
+
167
+ ### `simulatePayment`
168
+
169
+ ```typescript
170
+ components.simulatePayment();
171
+ ```
172
+
173
+ Calls the [simulate payment](https://docs.xendit.co/apidocs/simulate-payment-test-mode) endpoint.
174
+
175
+ This is only available in test mode sessions. It also requires the payment channel to be a QR, OTC, or VA channel, and it requires an action
176
+ to be in-progress.
177
+
178
+ ### `abortSubmission`
179
+
180
+ ```typescript
181
+ components.abortSubmission();
182
+ ```
183
+
184
+ Cancels the current submission, if any.
185
+
186
+ ### `destroyComponent`
187
+
188
+ ```typescript
189
+ components.destroyComponent(htmlElement);
190
+ ```
191
+
192
+ Destroys a component, deleting any cached data and removing the element from the document. Manual cleanup is not normally required,
193
+ but is made available if you want it.
194
+
195
+ ### `showValidationErrors`
196
+
197
+ ```typescript
198
+ components.showValidationErrors();
199
+ ```
200
+
201
+ Reveals hidden validation errors in the current channel's form, if any.
202
+
203
+ Validation errors are normally hidden until the user changes and unfocusses the input.
204
+
205
+ ### `getCurrentChannel`
206
+
207
+ ```typescript
208
+ const channel: XenditChannel = components.getCurrentChannel();
209
+ ```
210
+
211
+ Returns the current channel.
212
+
213
+ The current channel is the one you or the channel picker component selected by calling `createChannelComponent` or `setCurrentChannel`.
214
+
215
+ The current channel:
216
+
217
+ - Will be used for submission when you call `submit()`
218
+ - Is interactive (other channel components are disabled)
219
+
220
+ ### `setCurrentChannel`
221
+
222
+ ```typescript
223
+ const channel = components
224
+ .getActiveChannels()
225
+ .find((channel) => channel.channelCode === "CARDS");
226
+ if (channel) {
227
+ components.setCurrentChannel(channel);
228
+ }
229
+ ```
230
+
231
+ Makes the provided channel the current channel.
232
+
233
+ ## Events
234
+
235
+ ### `init`
236
+
237
+ Notifies you when the session information is loaded. Most SDK functions require the session to be loaded and can only be called after this event.
238
+
239
+ `createChannelPickerComponent` is available before the init event.
240
+
241
+ ### `session-complete` and `session-expired-or-canceled`
242
+
243
+ Notifies you when the session is in a terminal state.
244
+
245
+ `session-complete` means the session was successful, `session-expired-or-canceled` means the session was cancelled or expired.
246
+
247
+ ### `submission-ready` and `submission-not-ready`
248
+
249
+ Notifies you when the user is ready to submit the payment, meaning a channel is selected and all required information is collected.
250
+
251
+ `submit` will only work in the ready state, or it will throw. Calling it when there are form validation errors will also reveal those errors
252
+ to the user.
253
+
254
+ You might want to disable your submit button when not in the ready state.
255
+
256
+ ### `submission-begin` and `submission-end`
257
+
258
+ Notifies you when a submission is in progress.
259
+
260
+ You might want to show a pending state UI when in the submission state.
261
+
262
+ ### `action-begin` and `action-end`
263
+
264
+ Notifies you when an action is in progress.
265
+
266
+ You might want to create an action container in the action-begin event.
267
+
268
+ ## Appearance
269
+
270
+ ### CSS Variables
271
+
272
+ The Xendit Components SDK is customizable by overriding its CSS. The SDK inserts its CSS above any other CSS at the time of loading to allow it to be easily overridden.
273
+
274
+ CSS variables can be overridden to change styles across all components.
275
+
276
+ The following variables are available:
277
+ | Variable | Description |
278
+ | :- | -: |
279
+ | --xendit-font-family | Font applied to all xendit components |
280
+ | --xendit-color-primary | Accent color |
281
+ | --xendit-color-text | Base text color |
282
+ | --xendit-color-text-secondary | Lighter text color |
283
+ | --xendit-color-text-placeholder | Placeholder color |
284
+ | --xendit-color-disabled | Background color of disabled elements |
285
+ | --xendit-color-danger | Border color of elements with validation errors and text color of validation errors |
286
+ | --xendit-color-border | TODO: remove this |
287
+ | --xendit-color-border-subtle | TODO: rename this |
288
+ | --xendit-color-border-default | TODO: rename this |
289
+ | --xendit-color-background | Background color of elements |
290
+ | --xendit-focus-shadow | Box-shadow applied to elements with focus |
291
+ | --xendit-animation-duration | Duration of animations (affects the channel picker accordion) |
292
+ | --xendit-animation-ease | Ease function of animations |
293
+ | --xendit-radius-1 | Border radius applied to some components |
294
+ | --xendit-z-index-focus | Z-index applied to focused fields |
295
+
296
+ ### Appearance of Iframe fields
297
+
298
+ Some form fields (credit card inputs) are implemented inside iframes to protect the user's information.
299
+
300
+ Regular CSS doesn't apply inside iframes. The SDK instead provides overrides in the constructor which
301
+ it applies to the iframe fields.
302
+
303
+ ```typescript
304
+ const sdk = new XenditComponents({
305
+ appearance: {
306
+ // TODO
307
+ },
308
+ });
309
+ ```
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "xendit-components-web",
3
+ "version": "0.0.7",
4
+ "description": "Xendit frontend payment components SDK",
5
+ "type": "module",
6
+ "author": "Xendit",
7
+ "license": "UNLICENSED",
8
+ "files": [
9
+ "./sdk/dist/index.umd.js",
10
+ "./sdk/dist/index.umd.js.map",
11
+ "./sdk/dist/index.esm.js",
12
+ "./sdk/dist/index.esm.js.map",
13
+ "./sdk/dist/index.d.ts"
14
+ ],
15
+ "types": "./sdk/dist/index.d.ts",
16
+ "exports": {
17
+ "import": {
18
+ "types": "./sdk/dist/index.d.ts",
19
+ "default": "./sdk/dist/index.esm.js"
20
+ },
21
+ "require": {
22
+ "types": "./sdk/dist/index.d.ts",
23
+ "default": "./sdk/dist/index.umd.js"
24
+ }
25
+ },
26
+ "scripts": {
27
+ "dev": "./build.ts dev",
28
+ "build": "./build.ts prod",
29
+ "test": "vitest run",
30
+ "test:watch": "vitest",
31
+ "test:run": "vitest run",
32
+ "test:debug": "vitest run --printConsoleTrace=true --silent=false",
33
+ "lint": "eslint . --ext .ts,.tsx",
34
+ "lint:fix": "eslint . --fix --ext .ts,.tsx",
35
+ "prettier": "prettier . --check",
36
+ "prettier:fix": "prettier . --check --write",
37
+ "prepare": "husky"
38
+ },
39
+ "dependencies": {
40
+ "card-validator": "^10.0.3",
41
+ "classnames": "^2.5.1",
42
+ "credit-card-type": "^10.1.0",
43
+ "i18next": "^25.6.3",
44
+ "libphonenumber-js": "^1.12.29",
45
+ "preact": "^10.27.2",
46
+ "react-circle-flags": "^0.0.24",
47
+ "tslib": "^2.8.1"
48
+ },
49
+ "devDependencies": {
50
+ "@eslint/js": "^9.39.1",
51
+ "@microsoft/api-extractor": "^7.55.1",
52
+ "@preact/preset-vite": "^2.10.2",
53
+ "@rollup/plugin-alias": "^6.0.0",
54
+ "@rollup/plugin-commonjs": "^29.0.0",
55
+ "@rollup/plugin-json": "^6.1.0",
56
+ "@rollup/plugin-node-resolve": "^16.0.3",
57
+ "@rollup/plugin-replace": "^6.0.3",
58
+ "@rollup/plugin-terser": "^0.4.4",
59
+ "@rollup/plugin-typescript": "^12.3.0",
60
+ "@testing-library/dom": "^10.4.1",
61
+ "@testing-library/jest-dom": "^6.9.1",
62
+ "@testing-library/user-event": "^14.6.1",
63
+ "@types/mime-types": "^3.0.1",
64
+ "@types/node": "^24.10.1",
65
+ "@vitest/coverage-v8": "4.0.14",
66
+ "eslint": "^9.39.1",
67
+ "eslint-plugin-react-hooks": "^7.0.1",
68
+ "husky": "^9.1.7",
69
+ "jsdom": "^27.2.0",
70
+ "lint-staged": "^16.2.7",
71
+ "mime-types": "^3.0.2",
72
+ "prettier": "^3.6.2",
73
+ "rollup": "^4.53.3",
74
+ "rollup-plugin-import-css": "^4.1.2",
75
+ "typescript": "^5.9.3",
76
+ "typescript-eslint": "^8.48.0",
77
+ "vitest": "^4.0.14"
78
+ },
79
+ "packageManager": "pnpm@10.15.1"
80
+ }