Implement server-driven Payments

Use the Checkout Transactions and Payment Intents APIs for programmatic Payments.

Ask about this Page
Copy for LLM
View as Markdown

After completing this page, you should be able to:

  • Use the Transactions API to initiate Payments programmatically.

  • Apply the Payment Intents API to manage Payment actions.

  • Implement server-driven Payment flows for subscriptions and automated operations.

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 and recurring billing
  • Automated retries after a temporary failure
  • Back-office operations such as delayed capture or partial refunds
Recurring Orders 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 commercetools Checkout. You'll use the following two APIs:

  • Transactions API to start a Payment programmatically (without the Checkout UI)
  • Payment Intents API to manage what happens after the Payment has been authorized — including capturing, cancelling, or refunding

Transactions API

The Checkout Transactions API 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, 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 applies.
For full API reference including representations and endpoints, see Checkout Transactions API.

To start a payment, your BFF calls the Transactions API to link the Cart with a specific Payment Integration like Stripe, Adyen, or Paypal.

Create a transaction with the Transactions APIts
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:

Even though the Transactions API bypasses the visual Checkout UI, the Transactions API still uses the same underlying Payment lifecycle as a standard Checkout Session, including Authorization, Payment capture, Authorization cancellation, and Refund. commercetools 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 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.
To capture an amount that has been previously authorized, use the capturePayment action to settle the funds.
Capture a payment using the Payment Intents APIts
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.
Cancel an authorization using the Payment Intents APIts
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.
Refund a payment using the Payment Intents APIts
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.

Test your knowledge