# Implement server-driven Payments After a Customer completes the first checkout, subsequent Payment actions don't need to be customer-driven through an online checkout page. Many real-world business cases initiate Payment on the server side, such as in the following scenarios: - [Recurring Orders](/api/recurring-orders-overview.md) and recurring billing - Automated retries after a temporary failure - Back-office operations such as delayed capture or partial refunds [Recurring Orders](/api/recurring-orders-overview.md) handle the scheduling layer: defining recurrence intervals, generating Orders on schedule, and managing the Recurring Order lifecycle. Server-driven payments handle the execution layer: initiating and processing the actual Payment for each generated Order. These two concerns are complementary but independent. Server-driven payments let your backend securely take control of these scenarios while keeping consistency with UI-driven checkout flows. In this section of the module, you'll learn how to implement server-driven payments by using Checkout. You'll use the following two APIs: - [Transactions API](/checkout/transactions-api.md) to start a Payment programmatically (without the Checkout UI) - [Payment Intents API](/checkout/payment-intents-api.md) to manage what happens after the Payment has been authorized — including capturing, canceling, or refunding ## Transactions API The [Checkout Transactions API](/checkout/transactions-api.md) lets you initiate tokenized Payments without the Checkout UI by reusing a stored payment token from a previous Checkout Session. This is useful for [Recurring Orders](/api/recurring-orders-overview.md), warranty extensions, or phone orders where the Customer isn't interacting with the storefront directly. The API communicates with the same Payment Connectors used in standard Checkout flows, and the same [Payment lifecycle](/checkout/payments-lifecycle.md) applies. For full API reference including representations and endpoints, see [Checkout Transactions API](/checkout/transactions-api.md). To start a payment, your BFF calls the Transactions API to link the Cart with a specific Payment Integration like Stripe, Adyen, or Paypal. ```ts title="Create a transaction with the Transactions API" async createTransaction(input: { cartId: string; centAmount: number; currencyCode: string; paymentIntegrationId: string; applicationKey?: string; idempotencyKey?: string; }) { const token = await this.getCheckoutToken( `manage_checkout_transactions:${this.projectKey} manage_project:${this.projectKey}` ); const body = { application: { typeId: 'application', key: input.applicationKey ?? 'demo-key' }, cart: { typeId: 'cart', id: input.cartId }, transactionItems: [ { paymentIntegration: { typeId: 'payment-integration', id: input.paymentIntegrationId }, amount: { centAmount: input.centAmount, currencyCode: input.currencyCode }, }, ], }; const resp = await fetch(`${this.checkoutHost}/${this.projectKey}/transactions`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...(input.idempotencyKey ? { 'Idempotency-Key': input.idempotencyKey } : {}), }, body: JSON.stringify(body), }); if (!resp.ok) throw new Error(`Create transaction failed: ${resp.status} ${await resp.text()}`); return resp.json(); } ``` This code creates the **Transaction resource** and kicks off the Connector flow, which results in a **Payment resource** that you can use for the Intent API later to manage the rest of the Payment cycle. The following diagram shows how a tokenized Payment flow works by using the Checkout Transactions API. This flow lets you trigger secure, headless Payments-such as for Subscriptions or phone orders—by reusing a stored Payment token without involving the Checkout UI: ```mermaid sequenceDiagram participant Backend as Your Backend System participant Cart as commercetools Cart participant Checkout as Checkout Transactions API participant PSP as Payment Service Provider Backend->>Cart: Create/Update Cart Backend->>Checkout: Create Transaction with token Checkout->>PSP: Process payment using stored token PSP-->>Checkout: Payment success/failure Checkout-->>Backend: Payment status update ``` Even though the Transactions API bypasses the visual Checkout UI, the Transactions API still uses the same underlying [Payment lifecycle](/checkout/payments-lifecycle.md) as a standard Checkout Session, including [Authorization](/checkout/payments-lifecycle.md#authorization), [Payment capture](/checkout/payments-lifecycle.md#payment-capture), [Authorization cancellation](/checkout/payments-lifecycle.md#authorization-cancellation), and [Refund](/checkout/payments-lifecycle.md#refund). Checkout tracks and manages these stages. This consistency ensures the following benefits: - You can rely on the same **Messages and events** for downstream processing. - Payment statuses are updated in real time. - Any business logic or automation that depends on these events (like Order creation or notifications) continues to work seamlessly. In short, the Transactions API gives you full control and flexibility while preserving the trusted and robust Checkout backend behavior. ## Payment Intents API The [Checkout Payment Intents API](/checkout/payment-intents-api.md) lets you control what happens after a Payment has been authorized, whether the Payment was created through the Transactions API or a traditional checkout flow. You can capture funds (`capturePayment`), void an authorization (`cancelAuthorization`), or issue a refund (`refundPayment`). For full API reference including representations and endpoints, see [Checkout Payment Intents API](/checkout/payment-intents-api.md). To capture an amount that has been previously authorized, use the `capturePayment` action to settle the funds. ```ts title="Capture a payment using the Payment Intents API" async capturePayment(paymentId: string, centAmount?: number, currencyCode?: string) { const { hasAuthSuccess, fullAmount } = await this.getPaymentState(paymentId); if (!hasAuthSuccess) throw new Error('Payment is not authorized yet.'); const token = await this.getCheckoutToken( `manage_checkout_payment_intents:${this.projectKey} manage_project:${this.projectKey}` ); const amount = { centAmount: centAmount ?? fullAmount.centAmount, currencyCode: currencyCode ?? fullAmount.currencyCode, }; const r = await fetch(`${this.checkoutHost}/${this.projectKey}/payment-intents/${paymentId}`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ actions: [{ action: 'capturePayment', amount }] }), }); if (!r.ok) throw new Error(`Capture failed: ${r.status} ${await r.text()}`); return r.json(); } ``` If a customer cancels an Order before shipment or you need to cancel an Order due to fulfillment issues, you can cancel the authorization by using the `cancelAuthorization` action to release the funds. ```ts title="Cancel an authorization using the Payment Intents API" async cancelAuthorization(paymentId: string) { const token = await this.getCheckoutToken( `manage_checkout_payment_intents:${this.projectKey} manage_project:${this.projectKey}` ); const r = await fetch(`${this.checkoutHost}/${this.projectKey}/payment-intents/${paymentId}`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ actions: [{ action: 'cancelPayment' }] }), }); if (!r.ok) throw new Error(`Cancel authorization failed: ${r.status} ${await r.text()}`); return r.json(); } ``` Similarly, if a Customer returns an item, you can issue a partial or full refund against the captured Payment by using the `refundPayment` action. ```ts title="Refund a payment using the Payment Intents API" async refundPayment(paymentId: string, centAmount: number, currencyCode = 'EUR') { const token = await this.getCheckoutToken( `manage_checkout_payment_intents:${this.projectKey} manage_project:${this.projectKey}` ); const r = await fetch(`${this.checkoutHost}/${this.projectKey}/payment-intents/${paymentId}`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ actions: [{ action: 'refundPayment', amount: { centAmount, currencyCode } }] }), }); if (!r.ok) throw new Error(`Refund failed: ${r.status} ${await r.text()}`); return r.json(); } ``` ## Key takeaways - The Transactions API enables fully programmatic, headless Payments by letting you initiate tokenized Payments without the Checkout UI. - Store Payment tokens securely after an initial Checkout Session to reuse for future transactions like Subscriptions or recurring billing. - The Transactions API communicates with the same Payment Connectors used in standard Checkout flows, ensuring consistency in Payment lifecycle management. - The Payment Intents API provides control over post-authorization actions including capture, cancellation, and refunds. - Use `capturePayment` to settle authorized funds, typically after goods are shipped or services are delivered. - Use `cancelAuthorization` to release funds if an Order is cancelled before shipment or due to fulfillment issues. - Use `refundPayment` to return money to Customers after a Payment has been captured, supporting both partial and full refunds. - Server-driven Payments maintain the same Payment lifecycle stages as UI-driven checkouts, including Payment status updates and Checkout messages. ## Related pages - [Area overview page with navigation](/learning-implement-checkout.md) - [Previous page: Payment Lifecycle](/learning-implement-checkout/implement-commercetools-checkout/payment-lifecycle.md) - [Next page: Checkout Events and Messages](/learning-implement-checkout/implement-commercetools-checkout/checkout-events-and-messages.md) - [Search documentation and API specs](/search.md)