# Autopay Plugin



import Image from 'next/image';

The Autopay Plugin allows you to define invoice autopay logic for policyholders. When the plugin is executed, a payment will be created if a `PaymentCreateRequest` is contained in the return object.

Configuration [#configuration]

Adding a value for `autopayLeadDays` to an <ApiLink name="InstallmentPlanRef" /> configuration tells the platform how many days before an invoice's `dueTime` the Autopay Plugin should be executed. The value must be greater than 0, and can be specified as a decimal value for more precise timing. The default value is 1.

An `autopayTime` will be calculated for your target invoices based on the `autopayLeadDays` value and the invoice `dueTime`. If an invoice has more than one [installment](/features/billing/installments-and-installment-lattices), the invoice `autopayTime` will be the earliest `autopayTime` of its installments.

Here's an example of an `InstallmentPlanRef` configuration called `Quarterly`:

```json
{
	"displayName": "Quarterly",
	"cadence": "quarterly",
	"anchorMode": "termStartDay",
	"generateLeadDays": 14,
	"dueLeadDays": 0,
	"maxInstallmentsPerTerm": 1000,
	"autopayLeadDays": 2
}
```

Make sure to update your [installment settings](/features/billing/installment-settings) to use your `InstallmentPlanRef` configuration.

For example, you could specify a `defaultInstallmentPlan` in the product configuration:

```json
{
	"defaultInstallmentPlan": "Quarterly"
}
```

Implementation [#implementation]

Create a new Java class in the `src/main/java/com/socotra/deployment/customer` folder. All plugin code must be contained within this folder. We named our class `AutopayPluginImpl.java` in the example below, but you can name your class whatever you'd like.

Implement the `AutopayPlugin` interface and override the `autopay` method.

For example, this implementation pays the total remaining amount on an invoice:

```java
public class AutopayPluginImpl implements AutopayPlugin {
    private static final Logger log = LoggerFactory.getLogger(AutopayPluginImpl.class);

    // Pay the total remaining amount on an invoice

    @Override
    public AutopayPluginResponse autopay(AutopayRequest autopayRequest) {
        Invoice invoice = autopayRequest.invoice();

        log.info("Received autopay request for invoice: {}", invoice.locator());

        return AutopayPluginResponse.builder()
                .paymentRequest(
                        PaymentCreateRequest.builder()
                                .type("StandardPayment")
                                .amount(invoice.totalRemainingAmount().orElseThrow())
                                .currency(invoice.currency())
                                .paymentState(PaymentState.requested)
                                .targets(
                                        List.of(
                                                CreditItem.builder()
                                                        .containerLocator(invoice.locator())
                                                        .containerType(CreditContainerType.invoice)
                                                        .build()))
                                .data(
                                        Map.of(
                                                "note",
                                                "from AutopayPlugin",
                                                "payerFirstName",
                                                "John",
                                                "payerLastName",
                                                "Doe"))
                                .build())
                .nextRequestTime(Instant.now())
                .build();
    }
}
```

The method request object contains the invoice.

The method returns an `AutoPayPluginResponse` with `paymentRequest` and `nextRequestTime` properties.

The `PaymentCreateRequest` object contains the following fields:

* `type` - Refers to a <ApiLink name="PaymentRef" /> configuration
* `amount` - The payment amount
* `currency` - The payment currency
* `paymentState` - The state of the payment that will be created by the plugin
* `financialInstrumentLocator` - The locator of the financial instrument that will be used to make the payment
* `targets` - One or more invoices that will be paid
* `retryPlanName` - The name of the payment execution [retry plan](/features/billing/payment-execution-service#retry-plans)
* `data` - [Extension data](/configuration/data-extensions/overview) that will be associated with the payment

The `nextRequestTime` can be used to delay the execution of the [Payment Execution Service](/features/billing/payment-execution-service) after the Autopay Plugin is executed. In the example above, the Payment Execution Service will be executed immediately after the plugin is executed. If no value is provided for `nextRequestTime`, the Payment Execution Service will be executed immediately after the plugin is executed.

Execution [#execution]

The system will execute the Autopay Plugin when the `autopayTime` is reached for an invoice.

The following diagram illustrates autopay timing:

<Image src="/images/autopay/autopay-timing.png" alt="autopay timing" width={800} height={337} unoptimized />

For payments created in the `requested` state, the [Payment Execution Service](/features/billing/payment-execution-service) will be executed after the plugin is executed.

Accounts must specify a default financial instrument before the Payment Execution Service can process payments. To specify a default instrument, first call the <ApiLink name="createFinancialInstrument">Create a Financial Instrument</ApiLink> API endpoint, then call the <ApiLink name="setFinancialInstrumentAsDefault">Set the Default Financial Instrument for a Tenant</ApiLink> API endpoint.

After the Payment Execution Service is executed, the [Payment Post-Processing Plugin](/configuration/plugins/payment-post-processing) will be executed.

Once a payment moves to the `posted` state, corresponding invoices will move to the `settled` state as long as the invoices have been paid in full. Otherwise, invoices will remain in the `open` state.

Payment States [#payment-states]

The Autopay Plugin can be used to create a payment in a specific state. Payment processing behavior differs based on the state of the payment created by the plugin:

* `requested` - The Payment Execution Service will immediately attempt to process the payment through a payment provider such as Stripe or Braintree. Once the payment has been successfully processed, it will move to the `posted` state.
* `posted` - The Payment Execution Service will not attempt to process the payment. Creating a payment this way assumes that the Autopay Plugin code has successfully processed the payment by calling an external API via the [Java HttpClient ](https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html).
* `draft` - The Payment Execution Service will not attempt to process the payment. The [Payments API](/api/billing/payments) can be used to manually move the payment to a different state.

Payments can be created in other states in addition to the states listed above. Refer to the `paymentState` field in the <ApiLink name="PaymentResponse" /> object for a complete list of payment states.

Disabling Autopay [#disabling-autopay]

The Autopay Plugin can be disabled for an invoice by calling the <ApiLink name="updateInvoice" /> API endpoint and setting `suppressAutopay` to `true`.

Here's an example request:

```json
{
	"suppressAutopay": true
}
```

Updating Autopay Time [#updating-autopay-time]

The `autopayTime` can be manually updated for an invoice by calling the <ApiLink name="updateInvoice" /> API endpoint and specifying an `autopayTime`.

Here's an example request:

```json
{
	"autopayTime": "2025-07-10T05:00:00Z"
}
```

The `autopayTime` can be set to any time before the invoice `dueTime`. If the `autopayTime` is set to a time in the past, the Autopay Plugin will be executed immediately.

<Callout>
  If the <ApiLink name="updateInvoice" /> API endpoint is used to trigger the Autopay Plugin, and the target invoice is already in the `settled` state, the endpoint will return a `400` error.
</Callout>

Events [#events]

The Autopay Plugin can generate the following [events](/configuration/general-topics/events):

* `billing.invoice.autopay` - The Autopay Plugin executed successfully
* `billing.invoice.autopayfailed` - The Autopay Plugin failed
* `billing.payment.request` - Payment transitioned to the `requested` state
* `billing.payment.execute` - Payment transitioned to the `executing` state
* `billing.payment.cancel` - Payment transitioned to the `cancelled` state
* `billing.payment.fail` - Payment transitioned to the `failed` state
* `billing.payment.validationfailed` - Payment failed to transition from the `draft` state to the `validated` state

Example [#example]

The following example is based on the Prism configuration. Contact your Socotra representative for more information.

```java
// Pay the total remaining amount on an invoice if the invoice fee amount is close to 10

@Override
public AutopayPluginResponse autopay(AutopayRequest autopayRequest) {
    Invoice invoice = autopayRequest.invoice();

    log.info("Received autopay request for invoice: {}", invoice.locator());

    Optional<ULID> policyLocator = invoice.invoiceItems().iterator().next().policyLocator();

    if (policyLocator.isPresent()) {
        Policy policy = DataFetcher.getInstance().getPolicy(policyLocator.get());

        if (invoice.totalRemainingAmount().isPresent()
                && policy.invoiceFeeAmount().isPresent()
                && policy
                .invoiceFeeAmount()
                .get()
                .subtract(BigDecimal.TEN)
                .abs()
                .compareTo(new BigDecimal("0.01"))
                <= 0) {
            return AutopayPluginResponse.builder()
                    .paymentRequest(
                            PaymentCreateRequest.builder()
                                    .type("StandardPayment")
                                    .amount(invoice.totalRemainingAmount().orElseThrow())
                                    .currency(invoice.currency())
                                    .paymentState(PaymentState.requested)
                                    .targets(
                                            List.of(
                                                    CreditItem.builder()
                                                            .containerLocator(invoice.locator())
                                                            .containerType(CreditContainerType.invoice)
                                                            .build()))
                                    .data(
                                            Map.of(
                                                    "note",
                                                    "from AutopayPlugin",
                                                    "payerFirstName",
                                                    "John",
                                                    "payerLastName",
                                                    "Doe"))
                                    .build())
                    .nextRequestTime(Instant.now())
                    .build();

        } else {
            return AutopayPluginResponse.builder().build();
        }
    } else {
        return AutopayPluginResponse.builder().build();
    }
}
```

Next Steps [#next-steps]

* [Payment Post-Processing Plugin](/configuration/plugins/payment-post-processing)

See Also [#see-also]

* [Plugins Overview](/configuration/plugins/overview)
* <ApiLink name="InstallmentPlanRef" />
* [Installment Lattices](/features/billing/installments-and-installment-lattices)
* [Installment Settings](/features/billing/installment-settings)
* [Payment Execution Service](/features/billing/payment-execution-service)
* [Financial Instruments and External Cash Transactions API](/api/billing/financial-instruments)
* [Invoices API](/api/billing/invoices)
