Skip to content

PSP Integration

This guide describes the recommended pattern for building a Payment Service Provider (PSP) app that integrates with the Commerce app.

Overview

PSP apps handle payment processing for Commerce carts. The core responsibility is:

  1. Accept a payment from the customer (via your PSP's API)
  2. Create a Hantera Payment actor with an authorization
  3. Link the payment to the cart and complete it

Each PSP has its own flow (redirect, iframe, client-side SDK, etc.), but the cart completion step is the same.

The Payment Flow

Exposing a Checkout Ingress

PSP apps typically expose a checkout ingress at:

POST /ingress/commerce/carts/{cartId}/payment/{provider}

This ingress receives the cart ID and returns whatever the storefront needs to initiate the payment flow (redirect URL, client token, iframe snippet, etc.).

Completing the Cart

When your PSP confirms the payment is authorized, complete the cart with these steps:

Step 1: Create a Payment Actor

filtrera
let paymentResult = messageActor (
  'payment'
  'new'
  [{
    type = 'create'
    body = {
      providerKey = 'your-provider'
      currencyCode = 'SEK'
      externalReference = externalPaymentId
      commands = [{
        type = 'createAuthorization'
        authorizationNumber = externalPaymentId
        amount = orderAmount
        authorizationState = 'successful'
      },{
        type = 'generatePaymentNumberByPrefix'
        prefix = 'YOUR-PREFIX'
      }]
    }
  }]
)

Key fields:

  • providerKey — identifies your PSP (e.g., 'stripe', 'kustom')
  • externalReference — the ID from your PSP's API
  • amount — the authorized amount in the cart's currency
filtrera
let completeResult = messageActor (
  'ticket'
  cartId
  [{
    type = 'applyCommands'
    body = {
      commands = [{
        type = 'createRelation'
        relationKey = 'payments'
        nodeId = paymentResult.actorId
      }]
    }
  },{
    type = 'complete'
  }]
)

The createRelation links the payment to the cart. The complete message transitions the cart to completed state and triggers the cart-to-order rule, which creates the actual order.

Important

Always create the payment and link it before completing the cart. The order creation rules may need the payment data.

Reacting to Cart Changes

If your PSP needs to stay synchronized with the cart (e.g., updating order amounts in an external checkout), you can listen to the OnCartMutation custom hook. This hook fires after every successful cart render and provides the full rendered cart data.

See Hooks for details on how to listen and react to cart renders.

Handling Captures

For PSPs that require explicit capture (as opposed to direct charges), implement a rule that listens for OnPaymentCapture:

filtrera
param input: OnPaymentCapture

from input.payment.providerKey match
  'your-provider' |>
    from {
      effect = 'scheduleJob'
      definition = 'apps/your-app/capturePayment'
      at = now
      parameters = {
        externalReference = input.payment.externalReference
        amount = input.capture.amount
      }
    }

The capture job then calls your PSP's capture API.

Idempotency

PSP integrations should be idempotent:

  • Payment creation: Check for existing payments by externalReference before creating new ones
  • Cart completion: If the cart is already completed, the complete message is a no-op
  • Webhooks: PSPs may send the same webhook multiple times

See Also

© 2024 Hantera AB. All rights reserved.