-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into shopx-1683-update-upgrade-guides
- Loading branch information
Showing
11 changed files
with
315 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
--- | ||
title: Price calculations | ||
--- | ||
|
||
This document provides an overview of how various transaction amount fields in a `TransactionItem` are calculated and influenced. Additionally, it provides details on how `totalBalance` is calculated for Checkout and Order entities. | ||
|
||
## Transaction Amount Fields Overview | ||
|
||
The following fields are calculated based on transaction events and their types: | ||
|
||
- **`authorizedAmount`**: The total amount successfully authorized. | ||
- **`chargedAmount`**: The total amount successfully charged. | ||
- **`refundedAmount`**: The total amount successfully refunded. | ||
- **`canceledAmount`**: The total amount successfully canceled. | ||
- **`authorizePendingAmount`**: The total amount of pending authorizations. | ||
- **`chargePendingAmount`**: The total amount of pending charges. | ||
- **`refundPendingAmount`**: The total amount of pending refunds. | ||
- **`cancelPendingAmount`**: The total amount of pending cancellations. | ||
|
||
### Calculation process | ||
|
||
When the transaction amounts are recalculated, firstly all values are set to zero, | ||
then the calculations are applied in the following order: | ||
1. recalculate amounts based on events without PSP reference | ||
2. recalculate amounts based on `AUTHORIZATION` events | ||
3. recalculate amounts based on `CHARGE` events | ||
4. recalculate amounts based on `REFUND` events | ||
5. recalculate amounts based on `CANCEL` events | ||
At each step, the amounts are incrementally adjusted by adding or subtracting values | ||
calculated in the current step to the totals from the previous step. | ||
|
||
### Assumptions | ||
|
||
#### 1. Event Grouping: | ||
- Events are grouped based on their PSP reference and type of action (e.g., authorization, charge). | ||
- Grouping ensures that events of the same type and PSP reference are properly matched to calculate amounts accurately. | ||
#### 2. Pending Amounts: | ||
- Pending amounts (`authorizePendingAmount`, `chargePendingAmount`, `refundPendingAmount`, `cancelPendingAmount`) are increased only if a `REQUEST` event exists for the corresponding PSP reference. | ||
- If a success or failure event is also associated with the same PSP reference, the system assumes the requested amount has already been processed, and the pending amount will not be increased. | ||
#### 3. `SUCCESS` and `FAILURE` Events: | ||
- The transaction amount (e.g., `authorizedAmount`, `chargedAmount`) is increased only when a `SUCCESS` event exists. | ||
- If both a `SUCCESS` and `FAILURE` event are present for the same PSP reference, the system compares their creation timestamps: | ||
- If the `FAILURE` event is newer, the `SUCCESS` event is ignored. | ||
- If the `SUCCESS` event is newer, it is used for the calculations. | ||
#### 4. Events Without PSP References Resulting from `TransactionCreate` and `TransactionUpdate`: | ||
- Events without PSP references can result from using the [`TransactionCreate`](/api-reference/payments/mutations/transaction-create.mdx) | ||
or [`TransactionUpdate`](/api-reference/payments/mutations/transaction-update.mdx) mutations. These events are automatically created to ensure accurate transaction amount calculations. | ||
The following events might lack a PSP reference: | ||
- `AUTHORIZATION_SUCCESS` | ||
- `AUTHORIZATION_ADJUSTMENT` | ||
- `CHARGE_SUCCESS` | ||
- `CHARGE_BACK` | ||
- `REFUND_SUCCESS` | ||
- `REFUND_REVERSE` | ||
- `CANCEL_SUCCESS` | ||
#### 5. Adjustment Events: | ||
- The `AUTHORIZATION_ADJUSTMENT`, overrides previous amount from previous `AUTHORIZATION` events, this means that if an adjustment is present, | ||
older authorization events are ignored, and the adjusted amount becomes the new authorized value. | ||
|
||
This logic ensures that transaction amounts reflect the current state of the transaction based on its event history while accounting for both processed and pending actions. | ||
|
||
### Calculation details | ||
|
||
#### For events without `pspReference` | ||
|
||
The events that lacks the `pspReference` resulting from `TransactionCreate` and `TransactionUpdate` mutations are influence the transaction amounts as follow: | ||
- `AUTHORIZATION_SUCCESS` increases `authorizedAmount` by the event amount | ||
- `AUTHORIZATION_ADJUSTMENT` overrides the existing `authorizedAmount` by the even amount | ||
- `CHARGE_SUCCESS` increases `chargedAmount` by the event amount | ||
- `CHARGE_BACK` reduces `chargedAmount` by the event amount | ||
- `REFUND_SUCCESS` increases `refundedAmount` by the event amount | ||
- `REFUND_REVERSE` increases `chargedAmount` by the event amount | ||
- `CANCEL_SUCCESS` increases `canceledAmount` by the event amount | ||
|
||
#### For events with `pspReference` | ||
|
||
:::note | ||
Events like `X_REQUEST`, generated by Saleor when calling the payment app, are excluded from calculations until a corresponding event with a valid `psp_reference` is received from the payment app. | ||
For example, an `AUTHORIZATION_REQUEST` event will not affect the calculations until an `AUTHORIZATION_SUCCESS` or a similar event containing a `psp_reference` is returned by the payment app. | ||
::: | ||
|
||
The following calculation rules apply to events that share the same `pspReference`. | ||
|
||
#### Authorized Value ([`transactionItem.authorizedAmount`](/api-reference/payments/objects/transaction-item#transactionitemauthorizedamountmoney---)) | ||
- **GIVEN** there is an `AUTHORIZATION_SUCCESS` event | ||
- **WHEN** there is no `AUTHORIZATION_FAILURE` | ||
- **OR WHEN** the `AUTHORIZATION_FAILURE` event is older than `AUTHORIZATION_SUCCESS` | ||
- **THEN** value increases by the `AUTHORIZATION_SUCCESS` event's amount. | ||
- **GIVEN** an `AUTHORIZATION_ADJUSTMENT` event | ||
- **WHEN** it's the latest event of this type | ||
- **THEN** value is overwritten by the `AUTHORIZATION_ADJUSTMENT` event amount. | ||
- **GIVEN** there is a `CHARGE_REQUEST` or `CHARGE_SUCCESS` event: | ||
- **THEN** value is reduced by the event's amount. | ||
- **GIVEN** there is a `CANCEL_REQUEST` or `CANCEL_SUCCESS` event: | ||
- **THEN** value is reduced by the event's amount. | ||
- **GIVEN** all calculations were performed and we have result `authorizationAmount` | ||
- **WHEN** calculated `authorizationAmount` is below `0` | ||
- **THEN** `authorizationAmount` is set to `0` (it cannot be lower than 0) | ||
|
||
#### Authorization Pending Value ([`transactionItem.authorizePendingAmount`](/api-reference/payments/objects/transaction-item#transactionitemauthorizependingamountmoney---)) | ||
- **GIVEN** there is an `AUTHORIZATION_REQUEST` event: | ||
- **WHEN** there is no `AUTHORIZATION_FAILURE` or `AUTHORIZATION_SUCCESS` | ||
- **THEN** value increases by the `AUTHORIZATION_REQUEST` event's amount | ||
|
||
#### Charge Value ([`transactionItem.chargedAmount`](/api-reference/payments/objects/transaction-item#transactionitemchargedamountmoney---)) | ||
- **GIVEN** there is a `CHARGE_SUCCESS` event | ||
- **WHEN** there is no `CHARGE_FAILURE` | ||
- **OR WHEN** the `CHARGE_FAILURE` event is older than `CHARGE_SUCCESS` | ||
- **THEN** value increases by the `CHARGE_SUCCESS` event's amount. | ||
- **GIVEN** there is a `CHARGE_BACK` event | ||
- **THEN**value is reduced by the event's amount | ||
- **GIVEN** there is a `REFUND_REQUEST` or `REFUND_SUCCESS` event: | ||
- **THEN** value is reduced by the event's amount. | ||
- **GIVEN** there is a `REFUND_REVERSE`event: | ||
- **THEN** value is increased by the event's amount. | ||
- This value can be negative. | ||
|
||
:::note | ||
Please note that chargedAmount is not affected by `CANCEL` events, as these are solely used for matching with `AUTHORIZATION` events. | ||
::: | ||
|
||
#### Charge Pending Value ([`transactionItem.chargePendingAmount`](/api-reference/payments/objects/transaction-item#transactionitemchargependingamountmoney---)) | ||
- **GIVEN** there is a `CHARGE_REQUEST` event: | ||
- **WHEN** there is no `CHARGE_FAILURE` or `CHARGE_SUCCESS` | ||
- **THEN** value increases by the `CHARGE_REQUEST` event's amount | ||
|
||
|
||
#### Refunded Value ([`transactionItem.refundedAmount`](/api-reference/payments/objects/transaction-item#transactionitemrefundedamountmoney---)) | ||
- **GIVEN** there is a `REFUND_SUCCESS` event | ||
- **WHEN** there is no `REFUND_FAILURE` | ||
- **OR WHEN** the `REFUND_FAILURE` event is older than `REFUND_SUCCESS` | ||
- **THEN** value increases by the `REFUND_SUCCESS` event's amount. | ||
- **GIVEN** there is a `REFUND_REVERSE` event: | ||
- **THEN** value is reduced by the event's amount. | ||
|
||
:::note | ||
`CHARGE_BACK` events, which behave similarly to refunds by reducing the `chargedAmount`, are not included in `refundedAmount`. This is because they are initiated by the issuing bank, not the merchant. | ||
::: | ||
|
||
#### Refund Pending Value ([`transactionItem.refundPendingAmount`](/api-reference/payments/objects/transaction-item#transactionitemrefundpendingamountmoney---)) | ||
- **GIVEN** there is a `REFUND_REQUEST` event: | ||
- **WHEN** there is no `REFUND_FAILURE` or `REFUND_SUCCESS` | ||
- **THEN** value increases by the `REFUND_REQUEST` event's amount | ||
|
||
|
||
#### Canceled Value ([`transactionItem.canceledAmount`](/api-reference/payments/objects/transaction-item#transactionitemcanceledamountmoney---) ) | ||
- **GIVEN** there is a `CANCEL_SUCCESS` event | ||
- **WHEN** there is no `CANCEL_FAILURE` | ||
- **OR WHEN** the `CANCEL_FAILURE` event is older than `CANCEL_SUCCESS` | ||
- **THEN** value increases by the `CANCEL_SUCCESS` event's amount. | ||
|
||
#### Cancel Pending Value ([`transactionItem.cancelPendingAmount`](/api-reference/payments/objects/transaction-item#transactionitemcancelpendingamountmoney---)) | ||
- **GIVEN** there is a `CANCEL_REQUEST` event: | ||
- **WHEN** there is no `CANCEL_FAILURE` or `CANCEL_REQUEST` | ||
- **THEN** value increases by the `CANCEL_REQUEST` event's amount | ||
|
||
|
||
## Total Balance Calculation | ||
|
||
The `totalBalance` represents the difference between the expected total cost and the total amount charged (including pending charges). | ||
|
||
This balance indicates whether the customer has overpaid (positive balance) or underpaid (negative balance) for the order. | ||
|
||
Below is an explanation of how `totalBalance` is computed. | ||
|
||
### Total balance for Checkout ([`checkout.totalBalance`](/api-reference/checkout/objects/checkout#checkouttotalbalancemoney---)) | ||
|
||
For a Checkout, `totalBalance` reflects the remaining balance after considering the total checkout cost, charged amounts, and pending charges. | ||
It is calculated as the difference between the sum of all successful and pending charges across associated transactions and the checkout’s total price: | ||
|
||
``` | ||
totalBalance = totalCharged - checkout.totalPrice | ||
``` | ||
|
||
where: | ||
``` | ||
totalCharged = ( | ||
sum(transaction.chargedAmount for transaction in transactions) | ||
+ sum(transaction.chargePendingAmount for transaction in transactions) | ||
) | ||
``` | ||
|
||
### Total balance for Order ([`order.totalBalance`](/api-reference/orders/objects/order#ordertotalbalancemoney---)) | ||
|
||
For an Order, `totalBalance` represents the remaining balance after accounting for `order.totalPrice`, charged amounts, and granted refunds. | ||
It is calculated as the difference between the total charged (including pending charges) and the order cost adjusted for granted refunds: | ||
|
||
``` | ||
totalBalance = totalCharged - (order.totalPrice - totalGrantedRefund) | ||
``` | ||
|
||
where `totalCharged` is sum of all successful and pending charges across associated transactions: | ||
``` | ||
totalCharged = ( | ||
sum(transaction.amountCharged for transaction in transactions) | ||
+ sum(transaction.amountChargePending for transaction in transactions) | ||
) | ||
``` | ||
|
||
and `totalGrantedRefund` is a total of all refunds issued to the customer: | ||
``` | ||
totalGrantedRefund = sum(grantedRefund.amount for grantedRefund in grantedRefunds) | ||
``` |
Oops, something went wrong.