# Payment Post-Processing Plugin



The Payment Post-Processing Plugin allows you to update payment details after the [Payment Execution Service](/features/billing/payment-execution-service) attempts to process a payment. This plugin can also be used to specify when the Payment Execution Service will make another attempt to process a payment.

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 `PaymentPostProcessingPluginImpl.java` in the example below, but you can name your class whatever you'd like.

Implement the `PaymentPostProcessingPlugin` interface, and override the method corresponding to your target entity type.

For example, the following implementation moves a payment to the `cancelled` state, updates the payment amount, adds a note, and instructs the Payment Execution Service to make another attempt to process the payment in one minute.

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

    // Move a payment to the cancelled state, update the payment amount, add a note, and instruct the Payment Execution Service to make another attempt to process the payment in one minute

    @Override
    public PaymentPostProcessingResponse postProcess(PaymentPostProcessingRequest paymentPostProcessingRequest) {
        log.info("Received payment post-processing request for payment: {}", paymentPostProcessingRequest.context().paymentLocator());

        return PaymentPostProcessingResponse.builder()
                .paymentState(PaymentState.cancelled)
                .amount(paymentPostProcessingRequest.context().amount().add(new BigDecimal("0.1")))
                .note("Note from the Payment Post-Processing Plugin")
                .nextRequestTime(Instant.now().plus(1, ChronoUnit.MINUTES))
                .build();
    }
}
```

The request object contains the following data:

* `paymentLocator` - The payment locator
* `paymentRequestState` - The current state of the payment
* `amount` - The payment amount
* `currency` - The payment currency
* `financialInstrumentLocator` - The financial instrument locator
* `externalTransactionId` - The external transaction ID (if it exists)
* `data` - [Extension data](/configuration/data-extensions/overview) associated with the payment

The response object returns the following data:

* `nextRequestTime` - When the Payment Execution Service will make another attempt to process a payment (This will override the `hoursBetweenAttempts` value in the current [retry plan](/features/billing/payment-execution-service#retry-plans))
* `amount` - The updated payment amount
* `paymentState` - The updated payment state
* `note` - A note to add to the payment

The Payment Post-Processing Plugin will not be called under certain circumstances, such as when the payment provider system is unavailable or if credentials are outdated. Plugin execution will fail if the payment moves to the `executing` state or the `reversed` state.

If the Payment Post-Processing Plugin returns a `nextRequestTime`, retry attempts will continue, even if the number of retry attempts has already exceeded the number of retry attempts specified in the current retry plan.

<Callout>
  If the Payment Post-Processing Plugin fails, the payment will move to the `failed` state, even if the payment provider successfully processed the payment. Failure details and the response from the payment provider can be retrieved by calling the <ApiLink name="fetchPayment">Fetch a Payment</ApiLink> API endpoint. If a payment provider fails to process a payment, you should process the payment manually, move the payment to the `posted` state by calling the <ApiLink name="postPayment">Post a Payment</ApiLink> API endpoint, and resolve the cause of the failure to prevent future issues.
</Callout>

Example [#example]

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

```java
// If a payment is in the error state, or an error is included in the payment note, move the payment to the failed state, add a note, and instruct the Payment Execution Service to make another attempt to process the payment in one hour

@Override
public PaymentPostProcessingResponse postProcess(PaymentPostProcessingRequest paymentPostProcessingRequest) {
    log.info("Received payment post-processing request for payment: {}", paymentPostProcessingRequest.context().paymentLocator());

    if (paymentPostProcessingRequest.context().paymentRequestState() == PaymentRequestState.error ||
            paymentPostProcessingRequest.context().data().get("note").equals("Error encountered")) {
        return PaymentPostProcessingResponse.builder()
                .paymentState(PaymentState.failed)
                .note("Payment failed due to an error")
                .nextRequestTime(Instant.now().plus(1, ChronoUnit.HOURS))
                .build();
    } else {
        return PaymentPostProcessingResponse.builder()
                .note("No error detected")
                .build();
    }
}
```

Next Steps [#next-steps]

* [Cancellation Plugin](/configuration/plugins/cancellation)

See Also [#see-also]

* [Plugins Overview](/configuration/plugins/overview)
* [Payment Execution Service](/features/billing/payment-execution-service)
* [Data Extensions](/configuration/data-extensions/overview)
* [Retry Plans](/features/billing/payment-execution-service#retry-plans)
* [Payments API](/api/billing/payments)
