# Add the payment widget to your site
Load the Leapa widget, render a payment form, and read the result.
The Leapa widget is a custom HTML element that renders a payment form inside your own page. The payer’s browser sends card numbers straight to Leapa, so your site never stores or forwards them. This page takes you from an empty page to a working form.
This is what your payer sees. Ten lines of HTML put it on the page, and Leapa handles the card details, the bank’s identity check and the receipt:
Every attribute the element reads, with defaults and limits.
What the widget hands back when a payment succeeds or fails.
## Before you start
You need three things:
- A Leapa account with a currency account in the currency you want to charge
- An API key issued for that currency account, from your [account settings](/docs/web-application/create-your-account#generate-api-keys)
- A page whose HTML you can edit
Your API key is a JSON Web Token (JWT), a signed string that records a few facts about you. The widget reads two of them. Your merchant country decides which payment methods appear, and the key itself says whether it is live or test.
On every render the widget prints its environment and version to the browser
console, as `[LEAPA:ENV]: Test - version: v3.2.0`. Read that line to confirm
which key loaded. Switch to your live key only after both `onSuccess` and
`onFailure` do what you expect on your own page.
The widget serves two merchant countries:
- **Burundi**: card and [BurundiPay](/docs/widgets/burundipay)
- **Cameroon**: card
With a key issued for any other country, the widget shows the payer a banner instead of a form, and names the country on your console.
## Put the form on the page
Three steps: load the widget, place the element, listen for the result.
### Load the widget
Add both tags to the `` of your page:
```html
```
The script defines the element. The stylesheet is 48 bytes and holds one rule, which hides the element until the script claims it.
Both URLs name `v3.2.0`, and yours should too. A URL with no version in it
follows whatever Leapa published last, so a release you did not ask for can
change your checkout on a Tuesday morning. Pinning means you decide when
that happens.
Drop the stylesheet and your page shows an empty inline box for a moment,
laid out by your own CSS rather than by the widget.
### Add the element
Put the element where you want the form to appear:
```html
```
Three attributes are required in every mode: `api-key`, `mode` and `description`. Each mode adds one of its own: `amount` for `charge`, `invoice-id` for `invoice`, `currency` for `add`. Set `currency` anyway in the other two, because it is sent with the payment and it decides whether BurundiPay can appear.
The widget checks all of that before it renders. A rule that fails replaces the whole form with this banner:
The banner is deliberately vague, because the payer cannot fix an attribute and should not read your configuration. Open the console to find out which rule failed. Only the first failing rule is reported, so fix them one at a time.
The `id` matters when you place more than one widget on the same page. Give each one a different `id` so your event listeners can tell them apart.
### Read the result
The widget reports back through two events on the element: `onSuccess` when Leapa accepts the payment, and `onFailure` when it does not.
```html
```
The widget already shows the payer a green or red banner, so you do not have to render the outcome yourself. Use these events for the work only your site can do: store the customer ID you got back, mark an order paid, or send the payer to a thank-you page. [Events and responses](/docs/widgets/events) documents what `event.detail` holds in each case.
## What the card form asks for
The card form always collects the card number, expiry, security code and a billing address. On top of that it shows a first name, a last name and an email field.
Pass a real value for any of those three and the widget fills it in and hides the field:
```html
```
```html
```
The second form asks only for the card and the address. The widget sends the name and email with the payment:
Two more attributes change the form. `dob="true"` adds a date of birth field, which is otherwise absent. `tos="true"` adds a required checkbox linking to Leapa’s terms of service, above the button. [Widget attributes](/docs/widgets/attributes#how-the-widget-collects-payer-details) covers every combination.
## The three modes
The `mode` attribute decides what the form does when the payer submits it.
### Save a payment method with `add`
Use `add` to store a customer and a card for later, without taking money now:
```html
```
The widget creates the customer, saves the card, and returns both objects. When a customer with that email already exists, it adds the card to that customer instead of failing. To add a second card to a customer you already know, pass `customer-id` and leave `email` out.
Visa cards register through a zero-amount charge, which is why an `add` form still needs a currency.
### Take a payment with `charge`
Use `charge` to take money now. Add `amount` to the required set:
```html
```
The example above charges 25.00 USD. Each currency has a floor and a
ceiling, listed in [Amount limits](/docs/widgets/attributes#amount-limits).
An amount outside them shows a banner instead of the form.
### Pay an existing invoice with `invoice`
Use `invoice` when you have already created an invoice in Leapa and want the payer to settle it:
```html
```
`invoice-id` is required here. `amount` is not, but pass it anyway: BurundiPay puts the figure on the Pay button, so a payer approving a request sees what they are approving.
Invoice mode is also the only mode that offers BurundiPay. Both of its rails settle against an invoice, so a Burundi merchant charging directly sees card alone.
## Choose which payment methods appear
By default the widget offers every method the merchant’s country supports, and shows a chooser only when there is more than one:
Set `source-type` to pin one method and drop the chooser:
| Value | What the payer gets |
| --- | --- |
| `all` | Every method the country supports. This is the default |
| `card` | The card form only |
| `burundipay` | BurundiPay, with both of its rails on one screen |
| `burundipay-qr` | BurundiPay, pinned to the QR code |
| `burundipay-phone` | BurundiPay, pinned to the phone request |
A Cameroon merchant on the default gets a card form and no chooser, because Cameroon supports card alone. A Burundi merchant on a BIF invoice gets both tiles, if their account is enabled for instant bank payments.
Pin a method the country does not support and the widget shows a banner
rather than serving card in its place. That is deliberate: a page that
quietly serves something else hides a broken integration.
## Language and appearance
Set `lang` to translate every label, placeholder and error message. English and French are supported, and the widget follows the attribute when you change it after load:
```html
```
Set `theme` to `light`, `dark`, or `auto` to follow the payer’s device. Leaving it out gives you a transparent background that sits on whatever your page paints behind it. You can also recolour the form to match your brand with CSS custom properties. [Theming](/docs/widgets/theming) covers both.
## What 3D Secure means for your checkout
3D Secure (3DS) is an extra identity check that card networks run during a card payment. You know it by its brand names, Visa Secure and Mastercard Identity Check. The widget runs it for you on every card payment, and there is nothing to configure.
Card-not-present payments, meaning any payment where nobody swipes the physical card, are more open to fraud than in-person ones. 3DS shifts that risk by asking the bank to confirm the payer is the real cardholder before the payment goes through.
When the bank wants that confirmation, it takes over the screen and asks for a code it sent to the payer’s phone or email:
Not every payment is challenged. The bank decides, and a payment can pass without the payer seeing anything. Either way the widget waits for the outcome and then fires `onSuccess` or `onFailure`, so your code handles both paths the same way.
BurundiPay does not use 3DS. The payer approves in their bank app instead.
## Test before you go live
A test key runs against Leapa’s test environment and the card networks’ staging authentication service, so no real money moves. These are the gateway test card numbers:
| Card number | Pays as |
| --- | --- |
| `4456530000001096` | Visa |
| `5123450000000008` | Mastercard, in XAF |
Any future expiry date and any three-digit security code work with them.
On `localhost` the widget calls Leapa’s development API rather than the
production one. Use a key issued for that environment while you build, and
re-test on a deployed URL before you ship.
---
_Generated from the Leapa documentation at leapa.co/docs._