# Payment lifecycle You can control when Payments are captured, canceled, or refunded by using the Checkout Payment Intents API. Although Checkout handles Payment authorization and creates Orders, you must implement the business logic for triggering captures, cancellations, and refunds based on your fulfillment workflow. This page covers the key Payment lifecycle operations that you need to implement and the actions that Checkout handles automatically. ## Key lifecycle operations For reference definitions of each payment stage and how Checkout manages them, see [Payment lifecycle](/checkout/payments-lifecycle.md). This page focuses on what you need to implement. The Payment lifecycle consists of the following important operations: ### Authorization and capture After Checkout creates an Order and authorizes the Payment (reserving funds), you control when those funds are captured. You can use one of the following two approaches: - **Automated capture**: configure your payment service provider (PSP) to automatically capture Payments based on pre-defined rules or timelines. Verify whether your PSP supports automatic capture. Automatic capture isn't recommended for Adyen due to compatibility issues. - **Manual capture**: use the [Checkout Payment Intents API](/checkout/payment-intents-api.md#capture-payment) to trigger the `capturePayment` action in response to specific business events. For example, you might capture the Payment when the following events occur: - The Order is picked and is ready to be shipped. - Stock is allocated for the Order within your enterprise resource planning or Order management system. ```typescript title="Capture payment when order ships" import { checkoutApiRoot } from "./apiClient.js"; async function capturePayment(paymentId, amount) { try { const response = await checkoutApiRoot .paymentIntents() .withPaymentId({ paymentId }) .post({ body: { actions: [ { action: "capturePayment", amount, }, ], }, }) .execute(); console.log("Payment captured successfully:", response.body); return response; } catch (error) { console.error("Failed to capture payment:", error.message); throw error; } } // Trigger capture when order ships // Capturing might happen directly on the PSP from a backend system e.g. ERP or OMS await capturePayment( order.paymentInfo.payments[0].id, {centAmount: 50000, currencyCode: "EUR"} ); ``` ### Authorization cancellation If an Order is canceled before Payment capture, you must void the authorization to release the reserved funds. Use the Checkout Payment Intents API to request a cancellation from your PSP. Checkout updates the Order by adding a `CancelAuthorization` Transaction to the existing Payment. For more information, see [Cancel Payment](/checkout/payment-intents-api.md#cancel-payment). ```typescript title="Cancel authorization for canceled order" import { checkoutApiRoot } from "./apiClient.js"; async function cancelAuthorization(paymentId) { try { const response = await checkoutApiRoot .paymentIntents() .withPaymentId({ paymentId }) .post({ body: { actions: [ { action: "cancelPayment", }, ], }, }) .execute(); console.log("Authorization canceled successfully:", response.body); return response; } catch (error) { console.error("Failed to cancel authorization:", error.message); throw error; } } // Cancel authorization when customer cancels order await cancelAuthorization(order.paymentInfo.payments[0].id); ``` ### Refund Refunds return captured funds to Customers, for example due to returns, damaged goods, or other reimbursement scenarios. Refunds can be either partial or full, but the total refund amount can't exceed the initially captured amount. You can initiate refunds through the [Checkout Payment Intents API](/checkout/payment-intents-api.md#refund-payment) by using the `refundPayment` action. ```typescript title="Issue partial refund for returned items" import { checkoutApiRoot } from "./apiClient.js"; async function refundPayment(paymentId, amount) { try { const response = await checkoutApiRoot .paymentIntents() .withPaymentId({ paymentId }) .post({ body: { actions: [ { action: "refundPayment", amount, }, ], }, }) .execute(); console.log("Payment refunded successfully:", response.body); return response; } catch (error) { console.error("Failed to refund payment:", error.message); throw error; } } // Refund €20.00 for returned item (original order was €50.00) await refundPayment( order.paymentInfo.payments[0].id, {centAmount: 2000, currencyCode: "EUR"} // €20.00 ); ``` ## Automated reversals Checkout lets you set predicates to define conditions for automatically canceling or refunding a Payment if the Order creation fails. Automated Reversal Predicates operate by using a true/false logic and can use the values of the Cart fields, including Custom Fields. If the Automated Reversal predicate evaluates as `true`, then the Automated Reversal is processed for the relevant Payment. Automated Reversal Predicates are set on the application during or after Order creation. For more information about configuring predicates, see [Set predicates for automated reversals](/checkout/payment-integration-predicates.md#set-predicates-for-automated-reversals). ## Requirements to implement business logic Checkout creates the Order and manages the Payment lifecycle, including updating the Payment record with transactions and Payment status. You must implement the business logic for the following: - **Managing `orderState` transitions**: Checkout doesn't update the `orderState` field. You can determine when to transition Orders between `Open`, `Confirmed`, `Complete`, or `Canceled` States based on your fulfillment workflow. - **Triggering payment captures**: decide when to capture authorized Payments, such as when Inventory is allocated, Order is picked, or shipment is dispatched. - **Handling cancellations and refunds**: implement the logic to cancel authorizations or issue refunds in response to business events like Order cancellation and returns processing. - **Reconciliation and monitoring**: track Payment States, handle failed transactions, and implement retry logic for transient failures. You can view Payments created by Checkout in the Merchant Center from an Order's detail page. Go to **Orders > Order list**, select the Order, and then click the **Payments** tab to view the Payment method, PSP, Payment provider ID, amount planned, and all Payment Transactions. **Payment field mapping:** The following table shows how Payment fields in commercetools map to the Merchant Center Payment UI: | commercetools Payment field | Merchant Center Payment UI | | --- | --- | | `interfaceId` | Payment provider ID | | `transactions[0].id` | Transaction ID in Transactions list | | `transactions[0].interactionId` | Interaction ID in Transactions list | The Payment Provider ID represents the main identifier for the Payment on the PSP side (mapped to `interfaceId`). In the Transactions list, the Interaction ID displays the reference returned by the PSP for that specific operation. Depending on the PSP's logic, this value may match the Payment Provider ID (common for Authorizations) or differ for subsequent operations like Charges or Refunds. The following example shows a complete workflow that combines Payment capture with Order State management: ```typescript title="Update orderState after successful capture" import { apiRoot } from "./apiClient.js"; // Your Composable Commerce API client async function confirmOrderAfterCapture(orderId, version) { const order = await apiRoot .orders() .withId({ ID: orderId }) .post({ body: { version: version, actions: [ { action: "changeOrderState", orderState: "Confirmed", }, ], }, }) .execute(); return order.body; } // Workflow: Capture payment, then update orderState try { // 1. Capture the payment await capturePayment( order.paymentInfo.payments[0].id, { centAmount: 5000, currencyCode: "EUR" } ); // 2. Update orderState to Confirmed await confirmOrderAfterCapture(order.id, order.version); console.log("Order confirmed and payment captured"); } catch (error) { console.error("Failed to capture payment or update order", error); // Implement your error handling and retry logic } ``` ## Key takeaways - Checkout authorizes Payments and creates Orders, but you can control when Payments are captured by using the Checkout Payment Intents API. - Use manual capture to align Payment capture with your fulfillment workflow, such as when Inventory is allocated or the shipment is dispatched. - Cancel authorizations by using the `cancelPayment` action when Orders are canceled before capture. - Issue full or partial refunds by using the `refundPayment` action for returns or other refund scenarios. - Configure Automated Reversal predicates to automatically cancel or refund Payments if Order creation fails. - You are responsible for managing `orderState` transitions—Checkout doesn't update this field. - Implement error handling and retry logic for Payment operations to handle transient failures. - Track Payment States and reconcile transactions between Checkout, your PSP, and your Order management system. ## Related pages - [Area overview page with navigation](/learning-implement-checkout.md) - [Previous page: Build with the Checkout SDK](/learning-implement-checkout/implement-commercetools-checkout/build-with-the-checkout-sdk.md) - [Next page: Implement server-driven payments](/learning-implement-checkout/implement-commercetools-checkout/implement-server-driven-payments.md) - [Search documentation and API specs](/search.md)