# Pro-code payment experiences

If pro-code development, when you need full control over every aspect of the user experience, managed LWCs of FinDock Payment Experiences, **Pay Button** and **Payment Method Selector**, can be implemented in a custom LWC component. This approach simplifies some of the Payment API handling you would otherwise need to develop when building complete LWCs from scratch.

## Quick start with FinDock Labs

If you need some helping getting started, we have LWC templates and scripts available through [FinDock Labs](https://github.com/FinDockLabs/experience-cloud-lwc).

The repository contains building blocks to help you build custom Lightning Web Components (LWC) for digital payment experiences using Experience Cloud and FinDock's managed LWCS for Payment Experiences. You can deploy the repository content directly to your org as a development baseline.

## Using Pay Button in LWC

When building a custom LWC that leverage the Pay Button LWC, always keep in mind that your custom LWC must pass a complete [payment intent](/api/payment-api-v2/payment-intent) request to the Pay Button component.

The Pay Button properties you can manipulate with your custom LWC are:

* `button-label`: the label (default "Pay") on the payment form button
* `payment-intent`: the JSON payload that represents the [Payment Intent callout to the Payment API](/api/getting-started-with-the-payment-api-v2).
* `show-error`: boolean (default true) to hide the small red error message above the pay button. Useful when implementing your own error handling.
* `onResult`: an event handler that is called when the pay button returns a result from the Payment API callout. The result is an object with fields `paymentIntentId`, `redirectUrl`, `errorMessage`, and `statusCode`.


When the callout encounters an error, `errorMessage` and `statusCode` contain the specific error details returned from the Payment API. When the callout is successful, the payer is redirected automatically. You do not need to handle `paymentIntentId` and `redirectUrl` yourself.

**customComponent.html**

```html
<template>
    <c-pay-button
        button-label={buttonLabel}
        payment-intent={paymentIntent}
        show-error={showError}
        onresult={handleResult}
    ></c-pay-button>
    <template lwc:if={resultText}>
        <p>{resultText}</p>
    </template>
</template>
```

**customComponent.js**

```javascript
import { LightningElement } from 'lwc';

/**
 * Example Pay Button Wrapper
 *
 * A thin wrapper that renders the Pay Button with a hardcoded button label and
 * an example one-time payment intent payload, used to exercise the direct
 * payment intent path without Flow configuration.
 */
export default class CustomComponent extends LightningElement {
    buttonLabel = 'Pay now';
    showError = false;
    resultText;

    handleResult(event) {
        this.resultText = JSON.stringify(event.detail);
    }

    paymentIntent = {
        SuccessURL: 'https://example.com/success',
        FailureURL: 'https://example.com/failure',
        Payer: {
            Contact: {
                SalesforceFields: {
                    FirstName: 'Test',
                    LastName: 'Payment',
                    Email: 'test@findock.com',
                    MailingStreet: 'Any Street',
                    MailingCity: 'Any City',
                    MailingPostalCode: '00000',
                    MobilePhone: '012-3456789'
                }
            }
        },
        OneTime: {
            Amount: 10,
            CurrencyISOCode: 'EUR'
        },
        PaymentMethod: {
            Name: 'CreditCard',
            Processor: 'DummyExtension-PSP',
            Target: 'Account NL'
        }
    };
}
```

## Using Payment Method Selector in LWC

There are multiple ways to build your custom LWC for payment method selection. However, the configuration that needs to be passed to the Pay Button LWC must use a specific flat JSON structure. You can use the provided script to automatically build a configuration file based on an existing Salesforce org with FinDock fully configured.

### Building configuration files

Which methods appear in your custom LWC, and how they're configured, is defined statically in `paymentMethodConfiguration.js` configuration file. The configuration files contains an array for every payment method and processor combination you want to offer to payers. What the payer sees is rendered at runtime based on the configuration file.

You can either manually edit the configuration file or use the [script from FinDock Labs](https://github.com/FinDockLabs/experience-cloud-lwc#generating-the-config-from-your-org). If you need to build a configuration file without access to a Salesforce org, the following table describes each of the fields you need to modify.

| Field  | Description |
|  --- | --- |
| `paymentProcessor` | String. Name of the FinDock payment extension package for the processor (e.g. `PaymentHub-Stripe`). Maps to `PaymentMethod.Processor`. Source: `PaymentMethods[].Processors[].Name`. |
| `paymentMethod` | String. Name of the payment method. Maps to `PaymentMethod.Name` in the PaymentIntent. Source: `PaymentMethods[].Name` from `GET /PaymentMethods`. |
| `target` | String. Merchant account name. Maps to `PaymentMethod.Target`. Find it in FinDock Setup → Processors & Methods → Accounts tab. If empty, FinDock uses the configured default merchant account for the payment processor. |
| `enabledOneTime` | Boolean. Show this payment method for one-time payments. |
| `enabledRecurring` | Boolean. Show this method for recurring payments. If the method-processor combination does not support recurring payments, this setting is ignored. |
| `isDefaultOneTime` | Boolean. Show this payment method pre-selected for one-time payments. Exactly one entry may be `true`. |
| `isDefaultRecurring` | Boolean. Show this payment method pre-selected for recurring payments. Exactly one entry may be `true` for a payment method where `enabledRecurring` is also `true`. |
| `displayLabel` | String. Name of the payment method shown to the payer. Use a fixed value or a Custom Label reference (`labels.<name>`) so the label follows the interface language. Defaults to `paymentMethod` value if omitted. |
| `redirectInstruction` | String. Text shown to payer before being redirected to hosted payment page of PSP. Use a fixed value or a Custom Label reference (`labels.<name>`) to keep it translatable. Can be omitted when there is no redirect. |
| `parameters` | Array of processor-specific parameters. Use `null` or omit array when none. See next table for details. |


In addition to the above fields, the 'parameters' array can have one or more parameters, each with the following subset of fields.

| Field  | Description |
|  --- | --- |
| `name` | Parameter key (maps to `PaymentMethod.Parameters[name]`). Source: `Parameters[].Name` from `GET /PaymentMethods` |
| `value` | Value sent to the processor. Leave empty for payer-filled fields. |
| `visibleToCustomer` | Boolean. If `true`, the parameter is rendered for the payer to fill in. If `false` (default), the parameter is silently sent with `value` in the PaymentMethod bloc. |
| `displayLabel` | String. Label shown to the payer when `visibleToCustomer` is `true`. Use a fixed value or a Custom Label reference (`labels.<name>`) to support localization. Defaults to `name` if empty. |
| `required` | Boolean. An information field which indicates if the processor requires the parameter. |
| `description` | String. An informational field with an explanation of the parameter for internal guidance. |


For details on payment processors and related API parameters for supported methods, please refer to the processor-specific configuration article "Payment method parameters" section, such as [for Stripe](/docs/payment-processors/stripe#payment-method-parameters).

Below you can see an example configuration file with one payment processor, Stripe, and two payment methods.

```json
import {labels} from './paymentFormLabels';

export const PAYMENT_METHOD_CONFIG = [
    {
        paymentProcessor: 'PaymentHub-Stripe',
        paymentMethod: 'CreditCard',
        target: 'Stripe-Main-Account',
        enabledOneTime: true,
        enabledRecurring: true,
        isDefaultOneTime: true,
        isDefaultRecurring: false,
        displayLabel: 'CreditCard',
        parameters: [
            {
                name: 'locale',
                value: 'nl-NL',
                visibleToCustomer: false,
                description: 'Expected input: language tags as outlined on https://www.oracle.com/java/technologies/javase/jdk13locales.html. Examples: nl-NL, en-US.'
            },
            {
                name: 'description',
                value: '',
                visibleToCustomer: true,
                displayLabel: 'description',
                required: false,
                description: "Description of the payment for the payer's bank."
            }
        ]
    },
    {
        paymentProcessor: 'PaymentHub-Stripe',
        paymentMethod: 'Ideal',
        target: 'Stripe-Main-Account',
        enabledOneTime: true,
        enabledRecurring: false,
        isDefaultOneTime: false,
        isDefaultRecurring: false,
        displayLabel: 'iDEAL | Wero',
        redirectInstruction: 'You will be redirected to your bank to complete the payment.'
    }
];
```

### Handling the Payment Method block

The output of the payer's choices is a payment method, processor and target (merchant account) selection, along with any required and optional parameters for the given method-processor combination. The details build the `PaymentMethod` block of the Payment Intent request to the Payment API. This output feeds into the Pay Button, or, alternatively, directly into a callout to the PaymentIntent endpoint of the Payment API.

```mermaid
flowchart TB
    paySelect(["Custom Selector"])
    pms["Payment Method Selector"]
    payform(["Custom Pay Form"])
    pb["Pay Button"]

  %% ================= Edges =================
  paySelect -->|"Config file"| pms
  pms -->|"PaymentMethod<br/>block"| payform
  payform -->|"PaymentIntent<br/>(incl. PaymentMethod block)"| pb
  pb -->|"RedirectURL<br/>+ PaymentIntent Id"| payform

  %% ================= Styling =================
  style payform fill:#00A1E0, stroke:#24315E
  style paySelect fill:#00A1E0, stroke:#24315E
  style pms fill:#8282FC, stroke:#24315E
  style pb fill:#8282FC, stroke:#24315E
```

Your payment method selection configuration dynamically builds the `PaymentMethod` block of a payment intent request. If you use a custom LWC for your pay-action that may be used with payment selection step, remember that the `PaymentMethod` block must be passed in the payment intent to the Pay Button component.

## Error handling

If a managed FinDock component encounters an error, the event is broadcast to the following Salesforce event-handling channels:

* Lightning Web Component dispatch events
[Dispatch events](https://developer.salesforce.com/docs/platform/lwc/guide/events-create-dispatch.html) are best suited for advanced custom LWC development.
* Lightning Message Service
This [org-wide event channel](https://developer.salesforce.com/docs/platform/lwc/guide/use-message-channel.html) that can be used with custom font-end Salesforce development.
* Flow Attribute Change Event
This channel is a [good approach for reactive Screen Flows](https://developer.salesforce.com/docs/platform/lwc/guide/use-best-practices-reactivity.html)


Payment errors generate a `PAYMENT_ERROR` message with the following content.

**Message body**

| Field | Meaning |
|  --- | --- |
| `statusCode` | HTTP status of the PaymentIntent call. `200` on success, `422` when the request was well-formed but rejected (e.g. invalid data), other `4xx`/`5xx` on failure. |
| `errorCode` | FinDock error code, e.g. `202` (invalid IBAN). Used to route the error to a specific payment-method input. Null when the failure has no code. |
| `errorMessage` | Raw provider message (technical, locale-dependent). Prefer `errorLabel` for what you show the payer. |
| `errorLabel` | Payer-facing summary message, categorized server-side from the code (recoverable bank-detail issue, configuration problem, invalid data, or generic). |


See the full **Error and response codes** list in the [Payment API reference](/api/payment-api-v2/section/error-and-response-codes).