# Events and responses What onSuccess and onFailure send back, for each mode and payment method. The widget reports the outcome of a payment through two events on the `` element: `onSuccess` and `onFailure`. This page documents when each one fires and what `event.detail` holds. For the attributes that produce these payments, read [Widget attributes](/docs/widgets/attributes). The widget already shows the payer a banner for both outcomes, in place of the form: A green banner with a tick, reading “Payment received with BurundiPay”, where the payment form was So do not render the outcome yourself. Listen to these events for the work only your site can do: saving a customer ID, marking an order paid, or moving the payer to the next screen. ## Attach a listener Both events bubble and cross the widget’s [shadow boundary](/docs/widgets/theming), so you can listen on the element itself or on any ancestor: ```html ``` Both are `CustomEvent` objects, so the payload is always on `event.detail`. Attach the listener after the element exists in the page. Running your snippet before the widget script finishes loading is fine, because the events fire much later, when the payer submits. When you place more than one widget on a page, give each a different `id` and attach a listener to each. `onSuccess` fires once per payment. BurundiPay’s two rails both watch the same invoice, so the widget silences every announcement after the first. A payment you record is a payment that happened once. ## What onSuccess sends The shape of `event.detail` depends on the mode and on which method the payer used: | Payment | `event.detail` | | --- | --- | | Card, `add` mode | `{ customer, source }` | | Card, `charge` mode | `{ customer, charge }` | | Card, `invoice` mode | `{ customer, charge }` | | BurundiPay QR code | `{ amount, currency, charge_id, invoice_id, qr_reference }` | | BurundiPay phone request | `{ amount, currency, invoice_id, uetr }` | Card and BurundiPay return different shapes. In invoice mode the payer picks the method, so branch on what arrived rather than assuming one shape: ```html ``` ### The customer and source objects `add` mode returns the customer the widget created or found, plus the card it saved. Both match the objects the API returns from `POST /customers`: ```json { "object": "Customer", "id": "616820c258284cbbb87abe6fd9b9bd5a", "email": "bernice@example.com", "first_name": "Bernice", "last_name": "Lierne", "birth_date": "1990-04-12", "merchant_id": "99035d7b1bc644bbac854d72dc1ff73f", "address": { "object": "Address", "city": "Bujumbura" }, "status": "active", "is_live": false } ``` Store `customer.id`. You need it to charge the same payer later, either through the API or by passing it back to the widget as `customer-id`. The source object describes the saved card: ```json { "object": "account", "id": "2c72e224e39144598d7e8293dc7c09a1", "customer_id": "616820c258284cbbb87abe6fd9b9bd5a", "brand": "visa", "last_four": "1111", "exp_month": "03", "exp_year": "2028", "card_holder_name": "Bernice Lierne", "currency": "USD", "is_default": true, "status": "created" } ``` Leapa never returns the full card number. `last_four` and `brand` are what you show the payer when they pick a saved card later. ### The charge object Card payments in `charge` and `invoice` modes return a charge. The charge has the ID you reconcile against, the amount that moved, and the status: ```json { "id": "f3d1a2b45c6748e9a0b1c2d3e4f56789", "amount": 5000, "currency": "BIF", "status": "succeeded", "description": "Order 1234567890123", "failure_code": null, "failure_message": null, "source": { "brand": "visa", "last_four": "1111" } } ``` In `charge` mode `onSuccess` fires only when `status` is `succeeded`, so you do not have to check it again. Watch the amount unit for USD: you write `amount="25"` on the element, the widget sends 2500 to the API, and the charge comes back in that same minor unit. BIF and XAF have no minor unit, so they pass through unchanged. ### BurundiPay payloads BurundiPay settles an invoice rather than creating a customer, so its payload names the invoice and the reference for the rail that settled it. `qr_reference` identifies the QR code the payer scanned. `uetr` is the unique end-to-end transaction reference for a phone request, which is what your bank reconciliation matches on. Neither payload includes a customer, because BurundiPay never asks for one. ## What onFailure sends `onFailure` fires when the payment did not go through. Most failures include a code and a message: ```json { "code": "E9125", "message": "Payment declined by the issuer" } ``` BurundiPay uses the same shape, with `qr_generate_failed` and `rtp_request_failed` as the codes for a rail that could not start. One case sends nothing. When the bank declines a card outright, meaning the charge came back with a `failure_code`, the event fires with `event.detail` set to `undefined`. The reason still reaches the payer through the widget’s red banner; it does not reach your listener. Write the handler so it survives an empty payload: ```html ``` Do not build the payer’s error message from `event.detail`. The widget already shows them an accurate one, translated into the language you set with `lang`. Use the event to re-enable your own UI, log the attempt, or offer another payment method. A card the bank turns down fires `onFailure`, never `onSuccess`. A listener that reads `event.detail.charge.failure_code` on success will therefore never see a decline, and every payment it records will look like it worked. ## Common failure codes These are the decline reasons Leapa passes on from the card networks. The widget shows the payer a plain-language version. The codes are here so you can recognise them in your Leapa dashboard and in API responses: ## A worked example This listener records the payment on your own server and then sends the payer to a thank-you page: ```html ``` Send the charge ID to your server and check it against Leapa there before you release goods. A listener in the browser can be tampered with; your server calling Leapa cannot. --- _Generated from the Leapa documentation at leapa.co/docs._