Nomenclature
The following terms are used throughout this document:
- Seller: an online or offline merchant using the 35up platform API to offer its customers additional products as cross-selling items
- Vendor: an approved commercial partner by the 35up platform to offer matching products through the standardized API. The current list of vendors include: caseable, SellerX, Hama, smart mobile, Panzerglass, Libri, Neodigital and Clime
- Order: an order placed by the seller on the behalf of a customer, containing one or more 35up cross-selling items. Each order is given an universally unique alphanumeric id. This is the order entity in 35up’s database, not to be confused with the original order from the seller’s side, created by a customer, which exists only in their databases
- Order Item: each of the items included in the order entity described above
- Return: a collection of items meant to be returned to a single vendor
- Vendor Order: the purchase request sent by 35up to a vendor, in respect to one or more items included in a single order from a seller
- SKU: anachronism for “stock keeping unit”, used in the context of the API as the unique identifier of a fully qualified product in 35up’s unified catalog of product from all vendors
- Order Item Id: an unique id assigned by 35up to each Order Item in an Order
- State/Status: used as synonyms in 35up nomenclature, represent one of the possible conditions that an entity can be at a certain point in time. At the API level, they are designed as a finite (but extendable) set of string labels
Entities using State Machines
35up uses the concept of finite state machines (FSM) to control the state transition flow of several parts of the system. In particular, the following entities are currently using a FSM:
In addition to the above, the following entities are planned to use a FSM in the near future:
- Ticket (customer service)
- Dispute (customer service)
- Payment
- Shipment
All state labels are written in English language, lowercase and using underscores (snake_case).
Conceptual connection between State Machines
Internally, the 35up platform is designed following a distributed and event-driven architecture adhering to the established good practices in the industry.
When it comes to the state machines in the system, it means that they are independently guided, reacting and sending events that may be coming from other state machines or other sources, internally or externally initiated. With this practice, the size of each state machine is kept to a minimum and only the domain-specific knowledge is needed for each moving part.
An important side-effect of this design is that entity states and state transitions have clearer roles:
- an entity state usually tells what the system expects/accepts as next interactions and is often used as the subject for condition checking
- a state transition, besides moving an entity to another state, may trigger additional logic and events, causing automatic side-effects in other parts of the system, including new state machine transitions
Implementation Advise
Clients implementing a connection to 35up are advised to apply the same principles and design their systems in order to:
- use the entity states to check for conditions when interacting with other modules in their systems
- react to transitions (by detecting a state value change or by listening to an event) in a programmatic manner to propagate changes and trigger the necessary business logic automation
Also, keep in mind that despite being Finite State Machines, they are constantly evolving according to the business requirements. The addition of new state labels, although not frequent, is definitely possible. When designing data models to interact with API responses, the clients are advised to be careful if using strict Enum types mirroring the current labels. This data type is well known to break in case unknown values are received.
State Machine Description
Orders
Currently supported states are the following:
- pending: the order is created, but should not be processed yet
- approved: the order is ready to be processed
- processing: the order was picked up by the system and fulfillment actions already started
- fulfilled: all order items were already shipped by the vendors
- delivered: all order items were delivered to the final customer
- validating: conditions being checked or manual intervention required
- pending_cancellation: a cancellation was requested and some actions are still pending, such as returning an item back to the vendor, payment reimbursement, customer service complaint, etc
- cancelled: the order was successfully cancelled
- closed: the order is completed and the all the return/cancellation windows are closed
When creating a new order using POST /v1/orders or updating it through a PATCH /v1/orders/:order-id request, the following states are available for the seller:
- pending (default initial state on POST requests, can be omitted)
- approved (available as initial state on POST and also for PATCH requests)
- cancelled (available for PATCH requests)
The following transitions are currently possible by the sellers action by sending a PATCH /v1/orders/:order-id request with {"status": "target_status"} payload:
- pending -> approved: triggered by the seller in case an order was placed in pending state to mark it as ready to proceed. Usually this transition is used when certain conditions need to be checked by the seller on their side after the order is initially created. Important: if an order is placed in pending state it won’t be processed until it is moved to approved. Some examples are:
- the original order uses an asynchronous payment method, for example “bank transfer”. The order can be created at 35up in pending state together with the original order to “lock” the prices. After the bank transfer is received, the order is then patched to approved and 35up will proceed with the fulfillment
- the original order requires checking the customer’s credit score, such as selling a new mobile/data plan to which a new smartphone is attached. In case the contract is not approved, the cross-selling accessory would make no sense. An order in pending state prevents all fulfillment actions on 35up side’s until the seller is assures all the conditions are met
- pending, approved -> cancelled: triggered by the seller, in case the preconditions weren’t met on their side. Cancelling a pending or approved order has no condition checks and can be done at any time without costs
- processing, fulfilled, delivered -> cancelled: when an order was already picked up for processing, one or more fulfillment actions may already be performed by 35up and/or their partners. In such cases, different conditions have to be checked, depending on the actual state of each item included in the order. Because of that, a PATCH to cancelled will usually leave the order in pending_cancellation state, until all the conditions are checked
All the other transitions are triggered by 35up or its commercial partners in reaction to fulfillment actions and unconditional cancellations.
Order Items
The following states are currently defined for the order items:
- created: order item data was received and created in DB
- ordering: fulfillment has started with the order item information being transmitted to the vendor, but no confirmation was received yet
- ordered: order item information was successfully received by the vendor
- shipped: order item shipment information was received from the vendor
- delivered: order item was delivered to the final customer
- awaiting_return: a return was requested for this order item and is currently being being processed (check the respective return entity for details)
- returned: the return of the order item was successfully completed
- cancelled: the order item was successfully cancelled
- validating: conditions being checked or manual intervention required
- closed: the processing of the order item is completed and the its return/cancellation window is closed
When a new order is created using POST /v1/orders, all the required order items are automatically created in created state.
After initial creation, the following states can be used by the seller in PATCH /v1/orders/:order-id/items/:item-id requests:
The following transitions are currently possible by the sellers action on PATCH /v1/orders/:order-id request with {"status": "target_status"} payload:
- created -> cancelled: used by the seller for unconditional cancellation before any fulfillment action takes place, free of charge
The state awaiting_return can also be achieved from ordered, shipped and delivered states by the seller action indirectly, creating a new return entity including the desired order item. Please check the return entity below.
All the other transitions are triggered by 35up or its commercial partners in reaction to fulfillment actions and unconditional cancellations.
Returns
A return entity currently supports the following states:
- created: a new return request was received by the system (usually created by the seller)
- rejected: processing of the return request was not possible (e.g. return window already closed)
- awaiting_return: the seller already transmitted the necessary information to the customer, who should next give the items to the post office
- expired: exceeded the maximum allowed amount of time, while awaiting the customer to ship the items
- customer_shipped: confirmation was received, that the customer shipped the items
- seller_received: abnormal initial state for a return, in case the customer sends the items directly to the seller. The seller should use this state to create a new return and forward the received items to the vendor
- seller_shipped: the seller has shipped the package containing the items received directly from the customer to the vendor, in the case of an abnormal return described above
- voided: the package was shipped, but never received at the vendor after the expected amount of time
- vendor_received: the vendor received the package with returned items, but still has to check the contents. Also used by the vendor as initial return state, in case they receive the returned items directly from the customer
- confirmed: the vendor confirmed that the items were received in the expected state and accepted the return
- not_confirmed: the vendor reported problems with the received package contents and did not accept the return. A customer service dispute will be started to clarify
- awaiting_refund: after the return is confirmed or a dispute in resolved in favor of the customer, an external action should be taken to revert the payment in total or partially to the customer. Usually, a seller action is expected at this point. This state is also automatically applied when the maximum amount of time is exceeded by the vendor to confirm the return after vendor_received state
- refunded: the payment was successfully reverted in favor of the customer
- awaiting_dispute: there are issues in the return process that require human intervention and checking different conditions to either confirm or cancel the return process
- cancelled: the return was cancelled reverting the state of the items
- validating: conditions being checked or manual intervention required
- closed: the return was completed and no further actions are required
The usual return process, with the states described above, starts with the customer making contact with the seller, who requests a new return to 35up. The seller then transmits the shipment information to the customer, who ships the items directly to the vendor. After checking the items, the vendor confirms the return and the seller give the respective refund to the customer.
However, sometimes abnormal return flows also happen. Specially, there are cases where the customer sends the items directly to the seller or to the vendor without formally starting the return process at the 35up platform. In such cases, it is up to the seller or the vendor to create the return in a more advanced state:
- In case the items are directly received by the seller:
- the return should be created using seller_received state
- patch the status to seller_shipped when the package is given to the post office
- the return follows the normal flow from this point onward
- In case the items are directly received by the vendor:
- the return will be created in vendor_received state
- the return follows the normal flow from this point onward
A new return can be created by the seller’s action using POST /v1/returns endpoint containing one or more items of the same vendor for a given order. In case items from multiple vendors need to be return, several return entities should be created for the same order.
When creating a return, the following states can be used by the seller as initial states:
- created (default): when the items are physically with the customer and should be shipped to the vendor
- awaiting_return: the information allowing the customer to ship the package was already provided and the seller is now awaiting for the customer action (for example, a shipping label was already generated and sent to the customer)
- customer_shipped: the return can be created directly in this state in case the seller already knows that the package was shipped by the customer (for example, using an already existing integration with a reverse logistic provider)
- seller_received: when the items were received at the seller’s warehouse directly from the customer and should still be shipped to the vendor
- seller_shipped: when the items were received at the seller’s warehouse directly from the customer and were already shipped to the vendor
The return status can be updated using PATCH /v1/returns/:return-id with a {"status": "target_state"} payload. The following states are available to the seller in such request:
- customer_shipped, seller_received and seller_shipped as described above
- refunded: to confirm that a refund was already issue in favor of the customer, after the return is confirmed
- cancelled: in case the return is not needed or applicable anymore and should be stopped
Given the states above, the following transitions are allowed by the state machine and can be triggered by seller’s actions:
- created -> awaiting_return: used when return information is given to the customer
- created -> customer_shipped: used when the customer is able to create the shipment by themselves going directly to the post office
- created -> cancelled: when the return intention was manifested, but later retracted for any reason
- awaiting_return -> customer_shipped: when the seller received the information that the customer correctly delivered the package to the post office
- seller_received -> seller_shipped: used when the seller ship the items to the vendor, after receiving them directly from the customer
- seller_received -> cancelled: used to cancel the return request after receiving the items directly from the customer, without sending them to the vendor (e.g. decided to discard the products). Notice that in such case the order will still be considered as fulfilled and will be billed as usual
- awaiting_refund -> refunded: used to confirm that the payment reversal in favor to the customer was correctly done
All the other transitions are triggered by 35up or its commercial partners in reaction to fulfillment actions and unconditional cancellations.
Mixed or Conflicting States
Some entities have natural child entities, notably the Orders and Returns are connected to one or more Order Items. As such, there may be a mix of different states between order items belonging to the same order or return.
When this happens, the system will advance the state of the parent entity to the most advanced non-conflicting global state that is common to all items in the group and correctly represents the global state of the parent entity.
For example, consider an order containing two order items (item_0 and item_1) of two different vendors (vendor_x and vendor_y, respectively):
- when item_0 is picked up for fulfillment by vendor_x, the order will advance to processing state, because the processing of the order has globally started and the order can’t be unconditionally cancelled anymore. Technically, processing is a state that tells that a sequence of actions has started, therefor requires a single order item transition to be successfully triggered and it is non-conflicting with approved state
- when item_1 is also collected for ordering by vendor_y, the order will stay in processing
- when the first item is shipped by one of the vendors, the order will stay in processing state, this is because the order can’t be considered globally fulfilled yet. The most advanced non-conflicting state at this point is processing. Technically, fulfilled is a state that tells that a sequence of actions was completed and therefor requires all sub-entities to reach a minimum status in their state machines (in this case, shipped). The state fulfilled conflicts with earlier states in the order FSM (preventing the transition), but is non-conflicting with more advanced states in the order FSM
- when the second item is also shipped by the other vendor, the order will globally move to fulfilled state (as now all the items are at least in shipped state)
- if the first item would have been moved to delivered state before the second item could move to shipped, the order would have stayed in processing state, because there would not have happened any global conditions to move the order to a more advanced state. When the second item would move to shipped, the order would have advanced to fulfilled, even if the first item was already delivered, given that fulfilled would now be the most advanced non-conflicting global state for the order