Transactions
A transaction represents a payment instance created in Order or Checkout. It holds a list of events that make up the payment process. Each event has a type that describes the action taken on the transaction. You can see the complete list of events in the TransactionEventTypeEnum.
Besides the events, a transaction also contains other payment information, like the amount or currency.
Amount rounding
If the provided amount uses more decimal places than used currency, it will be rounded to the nearest value.
For example:
19.999 USDwill become20.00 USD10.2 JPYwill become10 JPY
Creating transactions
Transaction stores details of a payment transaction attached to an order or a checkout:
The transactionCreate mutation takes the following arguments:
id: The ID of the checkout or order.transaction: Input data required to create a new transaction object.transactionEvent: Data that defines a transaction event. It can be used to provide more context about the current state of the transaction.
The following example shows how you can use the transactionCreate mutation to create a new transaction.
The transaction was authorized, and the payment was made with a credit card. The actions that can be called from Saleor are: CANCEL and CHARGE.
The authorized amount is $99.
mutation TransactionCreate($id: ID!, $transaction: TransactionCreateInput!) {
transactionCreate(id: $id, transaction: $transaction) {
transaction {
id
}
}
}{
"id": "Q2hlY2tvdXQ6MWQzNmU5YzctYWEwYS00NzM5LTk0MGQtNzdjNmU4Mjc5YmQ0",
"transaction": {
"name": "Credit card",
"message": "Authorized",
"pspReference": "PSP-ref123",
"availableActions": [
"CANCEL",
"CHARGE"
],
"amountAuthorized": {
"currency": "USD",
"amount": 99
},
"externalUrl": "https://saleor.io/payment-id/123"
}
}{
"data": {
"transactionCreate": {
"transaction": {
"id": "VHJhbnNhY3Rpb25JdGVtOjE="
}
}
},
"extensions": {
"cost": {
"requestedQueryCost": 0,
"maximumAvailable": 50000
}
}
}Updating transactions
The transactionUpdate mutation allows updating the transaction details.
It takes the following arguments:
id: The ID of the transaction.transaction: Input data that will be used to update the transaction object.transactionEvent: Data that defines a transaction event. It can be used to provide more context about the current state of the transaction.
The following example shows how you can use the transactionUpdate mutation to update the transaction.
The available action is REFUND. The authorized funds are charged, so amountAuthorized is $0 and amountCharged is $99.
mutation TransactionUpdate($id: ID, $transaction: TransactionUpdateInput, $transactionEvent: TransactionEventInput) {
transactionUpdate(
id: $id
transaction: $transaction
transactionEvent: $transactionEvent
) {
transaction {
id
}
}
}{
"id": "VHJhbnNhY3Rpb25JdGVtOjE=",
"transaction": {
"name": "Credit card",
"message": "Authorized",
"pspReference": "PSP-ref123",
"availableActions": [
"REFUND"
],
"amountAuthorized": {
"currency": "USD",
"amount": 0
},
"amountCharged": {
"currency": "USD",
"amount": 99
}
},
"transactionEvent": {
"message": "Payment charged",
"pspReference": "PSP-ref123.charge"
}
}{
"data": {
"transactionUpdate": {
"transaction": {
"id": "VHJhbnNhY3Rpb25JdGVtOjE="
}
}
},
"extensions": {
"cost": {
"requestedQueryCost": 0,
"maximumAvailable": 50000
}
}
}Reporting actions for transactions
The transactionEventReport is used to
report a new transaction event. The newly created event will be used to recalculate the transaction’s amounts.
The mutation should be used for handling action requests or reporting any
changes that happened on the payment provider side (eg. asynchronous webhooks for delayed payment methods, chargebacks, disputes etc.).
It takes the following arguments:
id: The id of the transaction.type: Type of the reported action.amount: The amount of the reported action. The amount is rounded based on the given currency precision. It is mandatory for allREQUEST,SUCCESS,ACTION_REQUIRED, andREQUESTevents. For other events, if the amount is not provided, it will be calculated based on previous events with the same pspReference. If the amount cannot be determined, an error will be raised. (Refer to the additional details below ).pspReference: The reference assigned to the action.time: The time of the action.externalUrl: The URL for the staff user to check the details of the action on the payment provider’s page. This URL will be available in the Saleor Dashboard.message: Message related to the action. The maximum length is 512 characters; any text exceeding this limit will be truncated.availableActions: Current list of actions available for the transaction.
The following example shows how the transactionEventReport mutation is used to report an event
that happened for a given transaction.
The report is a success charge action, with 20 as an amount. The currency is the same as declared
for the transaction. Available action that can proceed for a transaction is REFUND.
The provided data will be used to create a new TransactionEvent object that will be included in the recalculation process.
mutation TransactionEventReport($amount: PositiveDecimal, $availableActions: [TransactionActionEnum!], $externalUrl: String, $id: ID, $message: String, $pspReference: String!, $time: DateTime, $type: TransactionEventTypeEnum!) {
transactionEventReport(
id: $id
type: $type
amount: $amount
pspReference: $pspReference
time: $time
externalUrl: $externalUrl
message: $message
availableActions: $availableActions
) {
errors {
field
code
}
alreadyProcessed
transaction {
id
}
transactionEvent {
id
}
}
}{
"id": "VHJhbnNhY3Rpb25JdGVtOjE=",
"type": "CHARGE_SUCCESS",
"amount": 20,
"pspReference": "psp-123",
"time": "2022-01-01",
"externalUrl": "https://saleor.io/event-details/123",
"message": "Charge completed",
"availableActions": [
"REFUND"
]
}In the response, Saleor returns:
alreadyProcessed- Defines if the reported event hasn’t been processed earlier. If there is an event with the samepspReference,amount, andtypeas the ones provided in the input mutation, Saleor will return it instead of creating a new one, and the flag will be set totrue.
transaction- Transaction that has been updated based on the received report.transactionEvent- TransactionEvent that has been created based on the received report.
Amount calculations for reporting an action with missing amount value
The amount value is required for the following event types:
AUTHORIZATION_SUCCESS,AUTHORIZATION_ADJUSTMENT,AUTHORIZATION_REQUEST,CHARGE_ACTION_REQUIRED,CHARGE_SUCCESS,CHARGE_REQUEST,REFUND_SUCCESS,REFUND_REQUEST,CANCEL_SUCCESS,CANCEL_REQUEST.
It’s optional for the rest of the events. In case of missing amount value the following rules are used to
calculate the amount:
- In case of missing amount for event
INFO, the 0 is used. - In case of missing amount for all
*_FAILURE, the amount is taken from the corresponding*_SUCCESSor*_REQUESTevent with the samepspReference. In case of multiple events, the value from the newest event is taken:- for
REFUND_FAILUREtheamountis taken from the newest event of one of the following types:REFUND_SUCCESS,REFUND_REQUEST,CHARGE_SUCCESS,CHARGE_FAILURE,CHARGE_REQUEST; - for
CHARGE_FAILUREtheamountis taken from the newest event of one of the following types:CHARGE_SUCCESS,CHARGE_REQUEST,AUTHORIZATION_SUCCESS,AUTHORIZATION_FAILURE,AUTHORIZATION_REQUEST; - for
AUTHORIZATION_FAILUREtheamountis taken from the newest event of one of the following types:AUTHORIZATION_SUCCESS,AUTHORIZATION_REQUEST; - for
CANCEL_FAILUREtheamountis taken from the newest event of one of the following types:CANCEL_SUCCESS,CANCEL_REQUEST,AUTHORIZATION_SUCCESS,AUTHORIZATION_FAILURE,AUTHORIZATION_REQUEST.
- for
- In case of
REFUND_REVERSEtheamountis taken from theREFUND_SUCCESSevent with the samepspReference. - In case of
CHARGEBACKtheamountis taken from theCHARGE_SUCCESSevent with the samepspReference. - If the specific event for the pspReference doesn’t exist, the error will be raised.
Events Not Requiring pspReference
The following events do not require a pspReference:
CHARGE_ACTION_REQUIREDAUTHORIZATION_ACTION_REQUIREDCHARGE_FAILUREAUTHORIZATION_FAILUREREFUND_FAILURECANCEL_FAILURE
If the pspReference is missing, the event will still be created but will be excluded from transaction amount recalculations.
Consequently, it will not impact the charge, refund, cancel, or authorize amounts.
Additionally, it will not affect any existing transactionEvent of the same type that has a pspReference set.
Handling action requests for transactions
An action request is called when a staff user or an app requests an action for a given transaction.
Two mutations can trigger the action on the app side:
-
transactionRequestAction: will also create a newTransactionEventwith one of the request type (AUTHORIZATION_REQUEST,CHARGE_REQUEST,REFUND_REQUEST,CANCEL_REQUEST),amountand theowner(User or App). Saleor will send a synchronous webhook dedicated to the actionTRANSACTION_CHARGE_REQUESTED,TRANSACTION_CANCELATION_REQUESTED,TRANSACTION_REFUND_REQUESTED -
transactionRequestRefundForGrantedRefund: will create a newTransactionEventwithREFUND_REQUESTtype,amountand theowner(User or App). Saleor will send a synchronous webhookTRANSACTION_REFUND_REQUESTED.OrderGrantedRefundwill be included in the webhook payload (if requested in a subscription query for the webhook). This mutation is useful when the payment provider requires details about lines that are related to refund action.
The response should contain at least pspReference of the action. The pspReference will be placed in the previously created event of …_REQUEST type.
Optionally the response can contain the details of the completed action.
More information about request webhooks can be found in the synchronous webhooks for transactions guide.
Asynchronously processing actions
When action is processed asynchronously on the payment provider side, the app should call the transactionActionRequest
mutation once it receives a webhook notification from the payment provider.
The diagram below shows an example of processing asynchronous refund action.
Synchronously processing the action
The app immediately receives the status of the requested action. It can provide the details of the action in response to the received Saleor webhook. The following webhook events
can accept action details in the response: TRANSACTION_CHARGE_REQUESTED,
TRANSACTION_CANCELATION_REQUESTED,
TRANSACTION_REFUND_REQUESTED.
The below diagram shows an example of processing synchronous refund action.
TransactionItem and TransactionEvents pspReference Matching
Both TransactionItem
and TransactionEvent
contain a pspReference field, which refers to the payment service provider reference.
- The
pspReferenceonTransactionItemidentifies the overall payment. - The
pspReferenceonTransactionEventidentifies specific actions that occurred within theTransaction.
Key Assumptions:
- For a specific
TransactionItem, only oneTransactionEventof a giventypeandpspReferencemay exist, except for the following types: - Only one
TransactionEventof typeAUTHORIZATION_SUCCESSis allowed to exist. If an update is needed, the existingAUTHORIZATION_SUCCESSevent can be modified using theAUTHORIZATION_ADJUSTMENTevent.
Setting the pspReference on the TransactionItem
The pspReference value in a TransactionItem may initially be empty if it is not provided
during the TransactionCreate mutation or as a result of the TransactionInitialize mutation.
The transactionItem.pspReference is updated during payment processing and reflects the pspReference from the most recent event received from the payment application.
For example:
• If a transaction is successful, the pspReference will be set based on the last SUCCESS event from the payment provider.
• If the event sequence changes or multiple events are received, the pspReference always represents the value from the latest processed event.
The pspReference can be updated through the transactionUpdate mutation if necessary.
Events with optional pspReference
Certain event types do not require a pspReference. For more details, see Events Not Requiring pspReference.
Handling TransactionEvent Reports for Existing Types
When a TransactionEvent is reported:
- Event Type Check: If the event type is
AUTHORIZATION_ACTION_REQUIRED,CHARGE_ACTION_REQUIRED, orINFO, a new event is always created. - Existing Event Check: For other types, the system searches for an existing event with the same
typeandpspReferenceassigned to theTransactionItemrequested in the input.- Amount Match: If an existing event is found, the system compares its amount to the reported event’s amount.
- If the amounts match, the existing event is reused, and the
transactionEvent.alreadyProcessedvalue is set toTrue. For more information about this field, refer to the Reporting actions for transactions. - If they do not match, an error is raised.
- If the amounts match, the existing event is reused, and the
- No Existing Event: If no matching event is found, a new one is created, except for
AUTHORIZATION_SUCCESS, which must be unique, attempting to create a duplicate raises an error.
- Amount Match: If an existing event is found, the system compares its amount to the reported event’s amount.
Storing payment method details
Saleor allows storing two types of payment method details:
CARDOTHER
These details are available on the TransactionItem.paymentMethodDetails field.
Payment method details can be stored in two ways:
- Via Transaction Webhooks
- Via Transaction Mutations
Via transaction webhooks
Apps can return paymentMethodDetails while processing payments using the following webhooks:
TRANSACTION_INITIALIZE_SESSIONTRANSACTION_PROCESS_SESSION
View details about saving via webhook
Via transaction mutations
The following mutations accept a paymentMethodDetails input field:
transactionEventReporttransactionCreatetransactionUpdate
Examples
Example: Storing card details via transactionEventReport:
mutation TransactionEventReport($amount: PositiveDecimal, $availableActions: [TransactionActionEnum!], $id: ID, $pspReference: String!, $type: TransactionEventTypeEnum!, $paymentMethodDetails: PaymentMethodDetailsInput) {
transactionEventReport(
id: $id
type: $type
amount: $amount
pspReference: $pspReference
availableActions: $availableActions
paymentMethodDetails: $paymentMethodDetails
) {
errors {
field
code
}
alreadyProcessed
transaction {
id
}
transactionEvent {
id
}
}
}{
"id": "VHJhbnNhY3Rpb25JdGVtOjE=",
"type": "CHARGE_SUCCESS",
"amount": 20,
"pspReference": "psp-123",
"paymentMethodDetails": {
"card": {
"name": "Payment name",
"brand": "BRAND",
"firstDigits": "1234",
"lastDigits": "4321",
"expMonth": 11,
"expYear": 2000
}
}
}Example: Storing non-card (other) details via transactionEventReport:
mutation TransactionEventReport($amount: PositiveDecimal, $id: ID, $pspReference: String!, $type: TransactionEventTypeEnum!, $paymentMethodDetails: PaymentMethodDetailsInput) {
transactionEventReport(
id: $id
type: $type
amount: $amount
pspReference: $pspReference
paymentMethodDetails: $paymentMethodDetails
) {
errors {
field
code
}
alreadyProcessed
transaction {
id
}
transactionEvent {
id
}
}
}{
"id": "VHJhbnNhY3Rpb25JdGVtOjE=",
"type": "CHARGE_SUCCESS",
"amount": 20,
"pspReference": "psp-123",
"paymentMethodDetails": {
"other": {
"name": "Cash"
}
}
}Recognized payment method brands
When payment method details include a recognized brand value, the Saleor Dashboard will display the corresponding brand logo alongside the transaction. Brand matching is case-insensitive.
Card payment methods (CARD type — brand field):
visa,mastercard,mcamerican_expressamexdiscoverjcbdiners_clubunionpaymaestrogivex
Other payment methods (OTHER type — name field):
To check all available logos, check this list. Integration should return brand matching file name without extension (e.g. braintree will load braintree.svg file).
For any other value, the Dashboard will display a generic payment method name.
Showing payment history to the customer
Added in Saleor 3.23.
Order.transactions requires the MANAGE_ORDERS or HANDLE_PAYMENTS permission, so a storefront cannot use it to tell the customer how their order was paid. Use Order.transactionSummaries instead: it is meant for storefronts that display the payment history of an order, for example on an order confirmation page or in the customer’s account.
The field requires no permission. Like billingAddress or userEmail, it is available to whoever can read the order.
Each entry is a TransactionSummary - a read-only projection of one transaction that exposes only:
createdAt- when the transaction was created.paymentMethodDetails- the payment method, using the samePaymentMethodDetailstypes asTransactionItem.authorizedAmount,authorizePendingAmount,chargedAmount,chargePendingAmount,refundedAmount,canceledAmount- the money that moved.
query OrderPaymentHistory($id: ID!) {
order(id: $id) {
number
transactionSummaries {
createdAt
paymentMethodDetails {
name
... on CardPaymentMethodDetails {
brand
}
}
chargedAmount {
amount
currency
}
refundedAmount {
amount
currency
}
}
}
}{
"data": {
"order": {
"number": "123",
"transactionSummaries": [
{
"createdAt": "2025-09-01T10:15:30+00:00",
"paymentMethodDetails": {
"name": "Credit card",
"brand": "visa"
},
"chargedAmount": { "amount": 99.0, "currency": "USD" },
"refundedAmount": { "amount": 0.0, "currency": "USD" }
}
]
}
}
}Two things are deliberately hidden from this field:
- Card digits and expiration date.
firstDigits,lastDigits,expMonthandexpYearofCardPaymentMethodDetailsare alwaysnullhere. The customer sees the payment method and the brand, and nothing that identifies the instrument. Staff and apps read the full card data throughOrder.transactionsas before. - Transactions that moved no money. A transaction whose amounts are all zero is an abandoned payment attempt. It carries no information for the customer, so it is not returned.
TransactionSummary also has no id, no events, no externalUrl and no available actions. If you need any of those, query Order.transactions with the required permission.
Webhooks
Follow Transactions Webhook Events guide.
Automatic Checkout Completion
The Checkout can be automatically converted into an Order once the related TransactionItems fully cover the Checkout.
A checkout is considered fully covered when the Checkout.authorizeStatus is set to FULL.
This occurs when the total of authorizedAmount, chargedAmount, authorizePendingAmount, and chargePendingAmount
from all transactions connected to the Checkout covers the checkout.totalPrice.
To enable the feature, adjust the channel checkout settings.
The automaticCompletion field in CheckoutSettingsInput provides the following options for configuring automatic checkout completion:
enabled: A boolean value to enable or disable automatic checkout completion. When set totrue, the system will attempt to complete checkouts automatically once they are fully covered.delay: An optional integer value (in minutes) that specifies the delay from last update before a checkout is automatically completed. If not provided, the default delay is 30 minutes.cutOffDate: An optional ISO 8601 timestamp that defines the earliest checkout creation time eligible for automatic completion. Checkouts created before this date will not be automatically completed. If not provided, the current time is used as the default.
Configuring automatic checkout completion
Use the automaticCompletion field to configure when checkouts should be automatically completed. The cutOffDate parameter allows you to safely enable this feature for new checkouts only, without affecting existing ones.
mutation UpdateChannel($id: ID!,$input: ChannelUpdateInput!){
channelUpdate(id: $id, input: $input){
channel{
id
checkoutSettings {
automaticCompletionDelay
automaticCompletionCutOffDate
}
}
errors{
field
code
message
}
}
}{
"id": "Q2hhbm5lbDox",
"input": {
"checkoutSettings": {
"automaticCompletion": {
"enabled": true,
"delay": 120,
"cutOffDate": "2025-11-01T00:00:00Z"
}
}
}
}Important behavior notes
Checkout validation: Checkouts will not be automatically completed if validation issues occur. Use the Checkout.problems query to identify issues. Common validation problems include:
- Unavailable items (missing product/variant listings, unavailable products, hidden products)
- Missing billing address
- Invalid voucher or gift card
- Invalid shipping method
- Inactive channel
See the Checkout Problems page for details.
Checkout lifecycle: Once a checkout is automatically completed:
- The
Checkoutobject is deleted and queries/mutations on it will no longer work - Running
checkoutCompleteon an already completed checkout returns the previously createdOrder - Ensure your storefront handles this flow correctly before enabling
Pending transactions: Checkouts are automatically completed when authorizeStatus reaches FULL, even if some transactions are still pending confirmation from the payment provider. A pending transaction might be rejected later, resulting in an unpaid order. Use Order Expiration to handle unpaid orders.