{"id":45631938,"url":"https://github.com/xendit/xendit-components-web","last_synced_at":"2026-04-29T06:02:14.793Z","repository":{"id":335999341,"uuid":"1017150834","full_name":"xendit/xendit-components-web","owner":"xendit","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-24T05:52:57.000Z","size":7191,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T06:25:50.109Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xendit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-10T05:29:05.000Z","updated_at":"2026-04-24T05:52:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/xendit/xendit-components-web","commit_stats":null,"previous_names":["xendit/xendit-components-web"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/xendit/xendit-components-web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xendit%2Fxendit-components-web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xendit%2Fxendit-components-web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xendit%2Fxendit-components-web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xendit%2Fxendit-components-web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xendit","download_url":"https://codeload.github.com/xendit/xendit-components-web/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xendit%2Fxendit-components-web/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32412890,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T05:20:56.964Z","status":"ssl_error","status_checked_at":"2026-04-29T05:19:54.749Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-24T00:19:24.704Z","updated_at":"2026-04-29T06:02:14.780Z","avatar_url":"https://github.com/xendit.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xendit-components-web\n\n[![npm badge](https://img.shields.io/npm/v/xendit-components-web)](https://www.npmjs.com/package/xendit-components-web) [![build status badge](https://img.shields.io/github/actions/workflow/status/xendit/xendit-components-web/build-and-test.yml)](https://github.com/xendit/xendit-components-web/actions/workflows/build-and-test.yml?query=branch%3Amain) [![coverage badge](https://img.shields.io/codecov/c/github/xendit/xendit-components-web)](https://app.codecov.io/gh/xendit/xendit-components-web)\n\nWith Xendit Components, you can build a checkout experience tightly integrated into your site, powered by the Xendit Sessions API.\n\nThis SDK provides UI components for payment method selection, user input (e.g. credit cards), and user actions (e.g. 3DS), all of which can be customized to match your site's look \u0026 feel.\n\nView a live demo [here](https://demo-store.xendit.co/) by choosing \"Xendit Components\". View the demo source code [here](https://github.com/xendit/demo-store/tree/main/src/integrations).\n\n## Installation\n\nInstall using npm:\n\n```sh\nnpm install xendit-components-web --save\n```\n\nOr load it directly from our CDN:\n\n```html\n\u003cscript src=\"https://assets.xendit.co/components/VERSION_NUMBER_HERE/index.umd.js\"\u003e\u003c/script\u003e\n```\n\nOur npm package includes TypeScript types. You also download the [.d.ts file](https://assets.xendit.co/components/VERSION_NUMBER_HERE/index.d.ts) directly.\n\n## Sessions\n\nThe 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\nof attempts to pay, one of which can be successful. A successful payment completes the session.\n\nSessions also abstract away any differences between channels, allowing you to write once, and accept payments from any channel Xendit supports.\n\nTwo types of sessions are available:\n\n- `PAY` sessions collect a payment and optionally save a payment token for later use\n- `SAVE` sessions save a payment token\n\n## Quick Start\n\nFirst, initialize the SDK either with either:\n\n- Use `XenditComponentsTest` to use mock payment methods for development and testing.\n- Use `XenditComponents` to connect to the Xendit backend (including Xendit's test mode). This requires a `componentsSdkKey` option, which you can get by calling the Create Session endpoint. Create a session from your server, passing the `components_sdk_key` property back to your frontend.\n\n```typescript\n// For frontend development, use XenditComponentsTest, this provides built-in mock payment methods\nconst components: XenditComponents = new XenditComponentsTest({});\n// For production or e2e testing, use XenditComponents, passing in the components_sdk_key from the Session object\nconst components: XenditComponents = new XenditComponents({ componentsSdkKey });\n\n// Create a channel picker component\nconst channelPicker: HTMLElement = components.createChannelPickerComponent();\n\n// Insert the channel picker into your document\nmyCheckoutPage.replaceChildren(channelPicker);\n\n// Call submit() when the user clicks your submit button\nmySubmitButton.addEventListener(\"click\", () =\u003e {\n  components.submit();\n});\n\n// Listen to the status of the session\ncomponents.addEventListener(\"session-complete\", () =\u003e {\n  alert(\"Payment Success\");\n});\ncomponents.addEventListener(\"session-expired-or-canceled\", () =\u003e {\n  alert(\"Payment cancelled or expired\");\n});\n```\n\n## Components API\n\n### `XenditComponents` and `XenditComponentsTest`\n\nConstructor functions.\n\nBoth have the same API. `XenditComponents` will connect to Xendit servers, while `XenditComponentsTest` provides mock payment channels.\n\n### `createChannelPickerComponent`\n\n```typescript\nconst htmlElement = components.createChannelPickerComponent();\nmyContainer.replaceChildren(htmlElement);\n```\n\nCreates a UI for the user to select a payment channel and fill any required information.\n\nThis returns a `HTMLElement`, which you need to insert into your document.\n\nThis method uses caching, it will always return the same channel picker element.\nChanging the current channel will update the channel picker UI, even if it's unmounted.\nIf you don't want this, use `destroyComponent.`\n\n### `getActiveChannels`\n\n```typescript\nconst channels = components.getActiveChannels();\n```\n\nReturns the list of channels available in this session.\n\n### `getActiveChannelGroups`\n\n```typescript\nconst groups = components.getActiveChannelGroups();\n```\n\nReturns a list of channel groups. This can be used to categorize channels by type, if you want to build\nyour own channel selection UI. Each channel has a `uiGroup` property which matches one group's `id` property.\n\n\u003c!--\n### `getActiveDigitalWallets`\n\n```typescript\nconst digitalWallets = components.getActiveDigitalWallets();\n```\n\nReturns a list of digital wallets available in this session.\n--\u003e\n\n### `createChannelComponent`\n\n```typescript\nconst channel = components.getActiveChannels({ filter: \"CARDS\" })[0];\nif (channel) {\n  const htmlElement = components.createChannelComponent(channel);\n  myContainer.replaceChildren(htmlElement);\n}\n```\n\nSelects a payment channel and creates a UI for the user to fill any required information. You\nneed to pass in the channel you want to use, as returned from getActiveChannels.\n\nThis returns a `HTMLElement`, which you need to insert into your document.\n\nThis method uses caching, it will always return the same element for the same channel, to preserve the\nvalues the user enters into any form fields. If you don't want that, use `destroyComponent`.\n\n\u003c!--\n### `createDigitalWalletComponent`\n\n```typescript\nconst htmlElement = components.createDigitalWalletComponent(\"GOOGLE_PAY\");\n```\n\nCreates a digital wallet CTA button for the specified digital wallet.\n\nAfter a user clicks the button and completes the digital wallet flow, a submission will automatically begin. This is equivalent to setting the channel used (using `setCurrentChannel()`), and then calling `submit()`. The same events will be fired as a normal submission.\n\nThis returns a `HTMLElement`, which you need to insert into your document.\n--\u003e\n\n### `createActionContainerComponent`\n\n```typescript\ncomponents.addEventListener(\"action-begin\", () =\u003e {\n  const htmlElement = components.createActionContainerComponent({\n    qrCode: {\n      qrCodeOnly: true,\n    },\n  });\n  myActionContainer.replaceChildren(htmlElement);\n});\n```\n\nCreates a container into which any additional actions (e.g. 3DS, QR Codes) will be rendered.\n\nThis is optional, if you don't create one, the SDK will create a modal with an action container for you.\nYou cannot create an action container during an action (i.e. after the `action-begin` event).\n\nThis returns a `HTMLElement`, which you need to insert into your document.\n\nThis method does not use caching.\n\n### `submit`\n\n```typescript\nfunction onSubmitButtonClick() {\n  components.submit();\n}\n```\n\nBegins submission for the active payment channel.\n\nCall this from the click event of your submit button.\n\nSubmission is only available when the session is active, a channel is made current by creating a channel component, any required information is collected, and\nanother submission is not in progress. Use the `submission-ready` and `submission-not-ready` events to know when submission is available.\n\nThis calls the [create payment request](https://docs.xendit.co/apidocs/create-payment-request)\nor [create payment token](https://docs.xendit.co/apidocs/create-payment-token) endpoint depending on the session type. You\nmay listen to the corresponding webhooks on your server.\n\n### `simulatePayment`\n\n```typescript\ncomponents.simulatePayment();\n```\n\nCalls the [simulate payment](https://docs.xendit.co/apidocs/simulate-payment-test-mode) endpoint.\n\nThis 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\nto be in-progress.\n\n### `abortSubmission`\n\n```typescript\ncomponents.abortSubmission();\n```\n\nCancels the current submission, if any.\n\n### `destroyComponent`\n\n```typescript\ncomponents.destroyComponent(htmlElement);\n```\n\nDestroys a component, deleting any cached data and removing the element from the document. Manual cleanup is not normally required,\nbut is made available if you want it.\n\n### `showValidationErrors`\n\n```typescript\ncomponents.showValidationErrors();\n```\n\nReveals hidden validation errors in the current channel's form, if any.\n\nValidation errors are normally hidden until the user changes and unfocusses the input.\n\n### `getCurrentChannel`\n\n```typescript\nconst channel: XenditChannel = components.getCurrentChannel();\n```\n\nReturns the current channel.\n\nThe current channel is the one you or the channel picker component selected by calling `createChannelComponent` or `setCurrentChannel`.\n\nThe current channel:\n\n- Will be used for submission when you call `submit()`\n- Is interactive (other channel components are disabled)\n\n### `setCurrentChannel`\n\n```typescript\nconst channel = components.getActiveChannels({ filter: \"CARDS\" })[0];\nif (channel) {\n  components.setCurrentChannel(channel);\n}\n```\n\nMakes the provided channel the current channel.\n\n### `pollImmediately`\n\n```typescript\ncomponents.pollImmediately();\n```\n\nImmediately poll for the status of a submission. Only applicable while a submission is ongoing. Useful for handling payment affirmation (e.g. I have made the payment) by the user.\n\n## Events\n\n### `init`\n\nNotifies you when the session information is loaded. Most SDK functions require the session to be loaded and can only be called after this event.\n\n`createChannelPickerComponent` is available before the init event.\n\n### `session-complete` and `session-expired-or-canceled`\n\nNotifies you when the session is in a terminal state.\n\n`session-complete` means the session was successful, `session-expired-or-canceled` means the session was cancelled or expired.\n\n### `session-pending` and `session-not-pending`\n\nNotifies you when the session is in the pending state. The pending state means a payment request or token will take some time to complete due to a manual approval flow. This affects FPX business payments. No other payment attempts can be made while in the pending state.\n\nYou might want to show a pending state UI when in the pending state.\n\n### `submission-ready` and `submission-not-ready`\n\nNotifies you when the user is ready to submit the payment, meaning a channel is selected and all required information is collected.\n\n`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\nto the user.\n\nYou might want to disable your submit button when not in the ready state.\n\n### `submission-begin` and `submission-end`\n\nNotifies you when a submission is in progress.\n\nYou might want to show a pending state UI when in the submission state, and allowing the action UI to show on top.\n\n### `action-begin` and `action-end`\n\nNotifies you when an action is in progress.\n\nOptionally, you can create an action container in the action-begin event. A default action container modal will be created if you don't.\n\n## Appearance\n\n### CSS\n\nThe Xendit Components SDK is designed to be customized with CSS. You can override any styles with your own CSS. The SDK's base is inserted above other stylesheets at the time of loading to allow it to be easily overridden.\n\nRefer to [styles.css](https://github.com/xendit/xendit-components-web/blob/main/sdk/src/styles.css) for all the overidable selectors.\n\n### CSS Variables\n\nSome CSS variables are provided to allow for easy customization. These are defined on `:root` and can be overridden by your own CSS.\n\nThe following variables are available:\n| Variable | Description |\n| :- | :- |\n| --xendit-font-family | Font applied to all xendit components |\n| --xendit-color-primary | Accent color |\n| --xendit-color-text | Base text color |\n| --xendit-color-text-secondary | Lighter text color |\n| --xendit-color-text-placeholder | Placeholder color |\n| --xendit-color-disabled | Background color of disabled elements |\n| --xendit-color-danger | Border color of elements with validation errors and text color of validation errors |\n| --xendit-color-border | Border color used on accordions, input fields, and logos |\n| --xendit-color-background | Background color of input fields |\n| --xendit-focus-shadow | Box-shadow applied to elements with focus |\n| --xendit-animation-duration | Duration of animations (affects the channel picker accordion) |\n| --xendit-animation-ease | Ease function of animations |\n| --xendit-radius-1 | Border radius applied to some components |\n| --xendit-z-index-focus | Z-index applied to focused fields |\n| --xendit-qr-foreground-color | The color used for QR codes (the black pixels). |\n| --xendit-qr-background-color | The color used as the background of QR codes (the white pixels). |\n\nAdditionally, the following variables are set at component scope, where applicable:\n| Variable | Description |\n| :- | :- |\n| --xendit-channel-brand-color | A brand color. Varies depending on the channel or action component where it's used. Will always be a dark color appropriate for use as a background behind white text. |\n\n### CSS In Iframe Fields\n\nSome form fields (credit card inputs) are implemented inside iframes to protect the user's information.\n\nYou can't override the CSS inside the iframe fields. Instead, you can pass some limited styles to the constructor\nwhich we'll pass along to the iframes.\n\n```typescript\nconst sdk = new XenditComponents({\n  iframeFieldAppearance: {\n    inputStyles: {\n      // apply styles to inputs within iframe fields\n      color: \"#000\",\n    },\n    placeholderStyles: {\n      // apply styles to input placeholders in iframe fields\n      color: \"#ccc\",\n    },\n  },\n});\n```\n\n### Fonts In Iframe Fields\n\nIframes can't inherit fonts you define on your page, so we allow you to load the font separately inside the iframe, by passing the font source to the constructor.\n\n```typescript\nconst sdk = new XenditComponents({\n  iframeFieldAppearance: {\n    inputStyles: {\n      // fontFamily: \"serif\", // this is ignored if a fontFace is provided\n    }\n    fontFace: {\n      // insert a @font-face rule inside iframe fields\n      source: \"url(https://example.com/my-font-file) format(woff2)\",\n      descriptors: { display: \"swap\" },\n    },\n  },\n});\n```\n\n\u003c!--\n## Digital Wallet Support\n\nAny available Digital Wallet integration is automatically added at the top of the channel picker component. You may also create a Digital Wallet button manually by calling `createDigitalWalletComponent(\"GOOGLE_PAY\")`.\n\nWhen the user pays using the digital wallet, the submission flow will automatically begin, the same as if you had called `submit()`.\n\nGoogle Pay is currently the only supported digital wallet.\n\n### Google Pay™\n\nGoogle Pay is available to Sessions that meet the following requirements:\n\n- Your Session country is a country where Google Pay is supported.\n- You have at least one Google Pay compatible payment method available in your session. (e.g. cards)\n- You've provided your Google MerchantID to Xendit on the Xendit Dashboard. (If you don't have a Google MerchantID, complete registration with the [Google Pay \u0026 Wallet Console](https://pay.google.com/business/console/?utm_source=devsite\u0026utm_medium=devsite\u0026utm_campaign=devsite) to provision one)\n- The Google Pay SDK is loaded using `\u003cscript async src=\"https://pay.google.com/gp/p/js/pay.js\"\u003e\u003c/script\u003e`. (You need to include the SDK yourself, we don't bundle it. There's no need to wait for it to finish loading, if it's still loading when you create the component, it'll have `display:none` until it finishes loading)\n- The user is using a compatible browser. (The component will have `display:none` if the user is using an incompatible browser)\n\nWe configure Google Pay for you, providing your MerchantID, merchant name, and the allowed payment methods, including allowed card networks and authentication methods.\n\nYou can customize the appearance of the button using the options parameter of `createDigitalWalletComponent`, it accepts the same options as the [Google Pay API](https://developers.google.com/pay/api/web/reference/request-objects#ButtonOptions).\n\nTo use Google Pay, you must adhere to the Google Pay and Wallet API's [Acceptable Use Policy](https://payments.developers.google.com/terms/aup) and accept the terms defined in the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos). Additionally, please ensure you follow the [Google Pay brand guidelines](https://developers.google.com/pay/api/web/guides/brand-guidelines).\n--\u003e\n\n## Troubleshooting\n\n### Usage with React Strict Mode\n\nSince React 18, Strict Mode will perform mount/unmount checks during development. This means React intentionally unmounts and remounts every component on its initial mount.\n\nIf you instantiate XenditComponents and add a listener for the `init` event in a side effect, this can cause a race condition where the event fires for the last instance before the first instance finishes mounting. Hence, you should remember to remove the `init` event listener in the effect cleanup function when Strict Mode is on.\n\n```typescript\nuseEffect(() =\u003e {\n  // ...\n  const handleInit = () =\u003e {\n    // ...\n  };\n  components.addEventListener(\"init\", handleInit);\n  return () =\u003e {\n    components.removeEventListener(\"init\", handleInit);\n  };\n}, []);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxendit%2Fxendit-components-web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxendit%2Fxendit-components-web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxendit%2Fxendit-components-web/lists"}