# Submission component

Learn how to configure the payout submission component for Web.

## Basic usage

### Minimal configuration

The payout submission component renders a "Withdraw with PayPal" or "Withdraw with Venmo" button that executes the payout. At minimum, you must specify the recipient wallet type.

```typescript
const submitComponent = pxpCheckoutSdk.create('payout-submission', {
  recipientWallet: "Paypal"
});
submitComponent.mount('payout-submit-container');
```

| Property | Description |
|  --- | --- |
| `recipientWallet`string (enum) | The wallet type for the payout. Determines button styling and payout destination.Possible values:`PayPal``Venmo` |


### Advanced configuration

For more control over the button's appearance and behaviour, you can configure custom options:

```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  submitText: "Confirm withdrawal",
  class: "custom-payout-button",
  
  styles: {
    base: {
      backgroundColor: "#FFC438",
      color: "#09090C",
      borderRadius: "8px",
      padding: "14px 28px",
      fontSize: "16px",
      fontWeight: "600"
    },
    hover: {
      backgroundColor: "#F7B731"
    },
    active: {
      opacity: "0.7"
    },
    focus: {
      outline: "2px solid #0070CC",
      outlineOffset: "2px"
    },
    disabled: {
      opacity: "0.5",
      cursor: "not-allowed"
    },
    loading: {
      opacity: "0.8"
    }
  },
  
  brandingImageConfig: {
    width: "60px",
    height: "auto"
  },
  
  onClick: (event) => {
    console.log("Withdraw button clicked");
  },
  
  onPrePayoutSubmit: async () => {
    const approved = await showConfirmationModal();
    return { isApproved: approved, note: "Withdrawal request" };
  },
  
  onPostPayout: (result) => {
    console.log("Payout successful:", result);
    showSuccessMessage("Withdrawal completed!");
  },
  
  onError: (error) => {
    console.error("Payout failed:", error);
    showErrorMessage("Withdrawal failed. Please try again.");
  }
};

const submitComponent = pxpCheckoutSdk.create('payout-submission', submitConfig);
submitComponent.mount('payout-submit-container');
```

| Property | Description |
|  --- | --- |
| `recipientWallet`string (enum) | The wallet type for the payout.Possible values:`PayPal``Venmo` |
| `submitText`string | Custom button text. Defaults to `"Withdraw with {recipientWallet}"`. |
| `class`string | Custom CSS class for the button. |
| `styles`object | Button styles for different states. |
| `brandingImageConfig`object | Configuration for the wallet logo dimensions. |


The payout execution mode (SDK-managed vs merchant-managed) is configured at SDK initialisation via `paypalConfig.payoutConfig.proceedPayoutWithSdk`, not at component level.

## Styling

### Default styling

The submission component renders with PayPal or Venmo branded styling by default.

PayPal defaults
```typescript
{
  base: { 
    borderRadius: '3px',
    backgroundColor: '#FFC438', 
    padding: '12px 24px',
    color: '#09090C', 
    border: 'none',
    fontFamily: 'Roboto, sans-serif',
    fontSize: '16px', 
    lineHeight: '18px', 
    fontWeight: '500' 
  },
  hover: { backgroundColor: '#F7B731' },
  disabled: { opacity: '0.5', cursor: 'not-allowed' },
  loading: { }
}
```

Venmo defaults
```typescript
{
  base: { 
    borderRadius: '3px',
    backgroundColor: '#008CFF', 
    padding: '12px 24px',
    color: '#FFFFFF', 
    border: 'none',
    fontFamily: 'Roboto, sans-serif',
    fontSize: '16px', 
    lineHeight: '18px', 
    fontWeight: '500' 
  },
  hover: { backgroundColor: '#0070CC' },
  disabled: { opacity: '0.5', cursor: 'not-allowed' },
  loading: { }
}
```

The component doesn't apply default `active` or `focus` styles. If you need these states styled, provide custom values in the `styles` configuration.

### Custom styling

You can override the default appearance by providing custom styles for each button state:

```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  styles: {
    base: {
      backgroundColor: "#003087",
      color: "#ffffff",
      borderRadius: "12px",
      padding: "16px 32px",
      fontSize: "18px",
      fontWeight: "bold",
      border: "none",
      cursor: "pointer",
      transition: "all 0.2s ease"
    },
    hover: {
      backgroundColor: "#001f5c",
      transform: "translateY(-1px)",
      boxShadow: "0 4px 12px rgba(0, 48, 135, 0.3)"
    },
    active: {
      transform: "translateY(0)",
      boxShadow: "0 2px 6px rgba(0, 48, 135, 0.2)"
    },
    focus: {
      outline: "3px solid #0070CC",
      outlineOffset: "3px"
    },
    disabled: {
      backgroundColor: "#cccccc",
      color: "#666666",
      cursor: "not-allowed",
      opacity: "0.7"
    },
    loading: {
      backgroundColor: "#001f5c",
      opacity: "0.9"
    }
  }
};
```

| Property | Description |
|  --- | --- |
| `styles.base`CSSProperties | The normal interactive state. Applied when the button is ready to be clicked. |
| `styles.hover`CSSProperties | The hover state. Applied when the cursor is over the button. |
| `styles.active`CSSProperties | The active state. Applied when the button is pressed. |
| `styles.focus`CSSProperties | The focus state. Applied when the button receives keyboard focus. |
| `styles.disabled`CSSProperties | The disabled state. Applied during initialisation or when payout isn't available. |
| `styles.loading`CSSProperties | The loading state. Applied during payout processing. |


### Branding image configuration

Customise the wallet logo dimensions:

```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  brandingImageConfig: {
    width: "80px",
    height: "auto"
  }
};
```

| Property | Description |
|  --- | --- |
| `brandingImageConfig.width`string | The width of the wallet logo. |
| `brandingImageConfig.height`string | The height of the wallet logo. |


## Event handling

The submission component provides event handlers to manage the payout flow:

```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  
  onClick: (event) => { },
  onPrePayoutSubmit: async () => { },
  onPostPayout: (result) => { },
  onError: (error) => { }
};
```

| Callback | Description |
|  --- | --- |
| `onClick`(event: Event) => void | Called when the button is clicked, before validation. |
| `onPrePayoutSubmit`() => Promise<{ isApproved: boolean; note?: string }> | Called after validation, before payout execution. Return `{ isApproved: false }` to cancel. |
| `onPostPayout`(result: any) => void | Called when the payout completes successfully. |
| `onError`(error: any) => void | Called when an error occurs during payout. |


### onPrePayoutSubmit

This callback allows you to show a confirmation dialog and optionally include a note with the payout:

```typescript
onPrePayoutSubmit: async () => {
  // Show custom confirmation modal
  const userConfirmed = await showConfirmationModal({
    title: "Confirm Withdrawal",
    message: `Withdraw $${amount} to your PayPal account?`,
    confirmText: "Confirm",
    cancelText: "Cancel"
  });
  
  if (!userConfirmed) {
    return { isApproved: false };
  }
  
  return { 
    isApproved: true,
    note: "Withdrawal initiated by customer"  // Optional note (max 4000 chars)
  };
}
```

The `note` field is optional and has a maximum length of 4000 characters. It will be included with the payout transaction for reference.

## Methods

### mount(containerId)

Renders the submission button in the specified container:

```typescript
const submitComponent = pxpCheckoutSdk.create('payout-submission', submitConfig);
submitComponent.mount('payout-submit-container');
```

### unmount()

Removes the component from the DOM:

```typescript
submitComponent.unmount();
```

## Examples

### Basic PayPal submission

A simple implementation for PayPal payouts:

```typescript
const submitComponent = pxpCheckoutSdk.create('payout-submission', {
  recipientWallet: "Paypal",
  onPostPayout: (result) => {
    window.location.href = `/success?txn=${result.transactionId}`;
  },
  onError: (error) => {
    alert("Withdrawal failed. Please try again.");
  }
});
submitComponent.mount('payout-submit-container');
```

### Venmo submission with custom styling

An implementation styled to match Venmo branding:

```typescript
const submitConfig = {
  recipientWallet: "Venmo",
  submitText: "Get paid with Venmo",
  styles: {
    base: {
      backgroundColor: "#008CFF",
      color: "#ffffff",
      borderRadius: "24px",
      padding: "14px 28px",
      fontSize: "16px",
      fontWeight: "600",
      border: "none"
    },
    hover: {
      backgroundColor: "#0070CC"
    }
  },
  onPostPayout: (result) => {
    showSuccessMessage("Venmo payout successful!");
  }
};

const submitComponent = pxpCheckoutSdk.create('payout-submission', submitConfig);
submitComponent.mount('payout-submit-container');
```

### Submission with confirmation dialog

An implementation with a custom approval flow:

```typescript
const submitConfig = {
  recipientWallet: "Paypal",
  submitText: "Withdraw Funds",
  
  onClick: (event) => {
    console.log("User clicked withdraw button");
    trackEvent("payout_button_clicked");
  },
  
  onPrePayoutSubmit: async () => {
    // Perform validation
    const balance = await checkUserBalance();
    if (balance < payoutAmount) {
      showError("Insufficient balance");
      return { isApproved: false };
    }
    
    // Show confirmation dialog
    const confirmed = await showModal({
      title: "Confirm Withdrawal",
      body: `
        Amount: $${payoutAmount}
        Destination: ${userPaypalEmail}
        Processing time: 1-2 business days
      `,
      confirmButton: "Confirm Withdrawal",
      cancelButton: "Cancel"
    });
    
    if (!confirmed) {
      return { isApproved: false };
    }
    
    return { 
      isApproved: true,
      note: `Withdrawal request for user ${userId}`
    };
  },
  
  onPostPayout: (result) => {
    // Update UI
    showSuccessMessage("Withdrawal successful!");
    
    // Track analytics
    trackEvent("payout_completed", {
      transactionId: result.transactionId,
      amount: payoutAmount
    });
    
    // Redirect after delay
    setTimeout(() => {
      window.location.href = `/account/transactions`;
    }, 2000);
  },
  
  onError: (error) => {
    console.error("Payout error:", error);
    showErrorMessage("Withdrawal failed. Please try again.");
    trackEvent("payout_failed", { error: error.message });
  }
};

const submitComponent = pxpCheckoutSdk.create('payout-submission', submitConfig);
submitComponent.mount('payout-submit-container');
```

### Complete payout page

A full example showing the submission component in context:

```typescript
import { PxpCheckout, IntentType, CurrencyType } from "@pxpio/web-components-sdk";

async function initializePayoutPage() {
  const sessionData = await getSessionDataFromBackend();
  const customerWallet = await getStoredCustomerWallet();
  const payoutAmount = 150.00;
  
  const pxpCheckoutSdk = PxpCheckout.initialize({
    environment: "test",
    session: sessionData,
    ownerId: "Unity",
    ownerType: "MerchantGroup",
    transactionData: {
      amount: payoutAmount,
      currency: "USD" as CurrencyType,
      entryType: "Ecom",
      intent: { paypal: IntentType.Payout },
      merchantTransactionId: crypto.randomUUID(),
      merchantTransactionDate: () => new Date().toISOString()
    },
    paypalConfig: {
      payoutConfig: {
        proceedPayoutWithSdk: true,
        paypalWallet: {
          email: customerWallet.email,
          payerId: customerWallet.payerId
        }
      }
    }
  });

  // Create components
  const amountComponent = pxpCheckoutSdk.create('payout-amount', {
    label: "Withdrawal Amount"
  });

  const receiverComponent = pxpCheckoutSdk.create('paypal-payout-receiver', {
    label: "PayPal Account",
    showMaskToggle: true
  });

  const submitComponent = pxpCheckoutSdk.create('payout-submission', {
    recipientWallet: "Paypal",
    submitText: "Withdraw to PayPal",
    
    styles: {
      base: {
        backgroundColor: "#FFC438",
        color: "#09090C",
        borderRadius: "8px",
        padding: "16px 32px",
        fontSize: "18px",
        fontWeight: "bold",
        width: "100%"
      }
    },
    
    onPrePayoutSubmit: async () => {
      const confirmed = await showConfirmationDialog(payoutAmount);
      return { isApproved: confirmed };
    },
    
    onPostPayout: (result) => {
      showSuccessAnimation();
      setTimeout(() => {
        window.location.href = `/success?txn=${result.transactionId}`;
      }, 1500);
    },
    
    onError: (error) => {
      showErrorToast(error.message);
    }
  });

  // Mount all components
  amountComponent.mount('payout-amount-container');
  receiverComponent.mount('payout-receiver-container');
  submitComponent.mount('payout-submit-container');
}

initializePayoutPage();
```

```html
<div class="payout-page">
  <h1>Withdraw Funds</h1>
  <div class="payout-card">
    <div id="payout-amount-container"></div>
    <div id="payout-receiver-container"></div>
    <div id="payout-submit-container"></div>
  </div>
</div>
```

## Error handling

The component handles validation errors and payout failures through the `onError` callback:

```typescript
onError: (error) => {
  console.error("Payout error:", error);
  
  // Handle specific error types
  switch (error.ErrorCode) {
    case "SDK0809":
      showError("PayPal account information is missing.");
      break;
    case "SDK0810":
    case "SDK0811":
    case "SDK0812":
      showError("Invalid payout amount.");
      break;
    case "SDK0813":
    case "SDK0814":
    case "SDK0815":
      showError("Invalid currency.");
      break;
    case "SDK1001":
      showError("Recipient wallet is required.");
      break;
    case "SDK1003":
      showError("Unsupported wallet type.");
      break;
    default:
      showError("An error occurred. Please try again or contact support.");
  }
}
```

For a complete list of error codes, see [Data validation](/guides/checkout/components/web/paypal/payouts/data-validation).