# Transactions
Transactions are how you generate value on your platform, generally with Assets.
An Asset can be assigned to a User, in other words booked by this User, for some duration and/or quantity defined in Transaction object.
Transaction object can impact Asset availability exactly like Availability objects. This means changes in Search results and ability to create other Transactions with the same Asset.
Availabilities are generally created by the Asset owner, while Transactions are best suited to be created by other Users.
# Transactions and payments
Transactions can be your single source of truth to proceed with payments, transactional emails, or anything related to your platform transactional process.
Using the payment provider of your choice, such as Stripe, you can store payment info in metadata
or platformData
attributes.
Please make sure you’re not storing any sensitive data such as credit card number.
# Creating a Transaction
Let’s create a Transaction locking a single unit of an Asset, for one day:
await stelace.transactions.create({
assetId: 'ast_2l7fQps1I3a1gJYz2I3a',
startDate: '2019-09-13T00:00:00.000Z',
duration: { d: 1 },
quantity: 1
})
Supposing the Asset is timeBased
according to its Asset Type.
Transaction takerId
will be automatically set to currently authenticated User ID if applicable (publishable API key needed).
You can define a default currency
for all your Assets:
await stelace.config.update({
stelace: {
instant: {
currency: 'USD'
}
}
})
During a Transaction creation, the default currency
will be used, unless the Asset has a different currency
.
Asset currency
must be specified before creating the Transaction to apply.
# Availability check
When creating a Transaction, Stelace handles availability check for you.
Existing Availability and Transaction objects can block the period or quantity required.
If the following Availability object is created,
await stelace.availabilities.create({
assetId: 'ast_2l7fQps1I3a1gJYz2I3a',
startDate: '2019-09-14T00:00:00.000Z',
endDate: '2019-09-16T00:00:00.000Z',
quantity: 0
})
the request below will generate an error because the quantity
over the period is higher than the available quantity.
const transaction = await stelace.transactions.create({
assetId: 'ast_2l7fQps1I3a1gJYz2I3a',
startDate: '2019-09-13T00:00:00.000Z',
duration: { d: 3 },
quantity: 1
})
# Transaction process
Stelace lets you configure how Transactions can transition from one status to another, and how they impact the Availability of your Assets.
Each Transaction object has a status
property representing its current state in your business process.
Transitions define how Transaction status can change depending on your platform requirements.
A simple e-commerce platform might use the following statuses:
draft
- initial steppaid
- blocking availabilitycompleted
- blocking availability, final stepcancelled
- final step
Transactions impact Availability
depending on Asset Type unavailableWhen
property, and lock the quantity during the Transaction period if the Asset Type is timeBased
.
# Default Transaction process
Here is the transactionProcess
applied to every Asset Type by default:
{
initStatus: 'draft',
cancelStatus: 'cancelled',
transitions: [
{ name: 'accept', from: 'draft', to: 'accepted', actors: ['owner'] },
{ name: 'confirm', from: 'draft', to: 'confirmed', actors: ['taker'] },
{ name: 'pay', from: 'draft', to: 'pending-acceptance', actors: ['taker'] },
{ name: 'confirmAndPay', from: 'draft', to: 'pending-acceptance', actors: ['taker'] },
{ name: 'pay', from: 'confirmed', to: 'pending-acceptance', actors: ['taker'] },
{ name: 'accept', from: 'confirmed', to: 'pending-payment', actors: ['owner'] },
{ name: 'pay', from: 'accepted', to: 'validated', actors: ['taker'] },
{ name: 'confirmAndPay', from: 'accepted', to: 'validated', actors: ['taker'] },
{ name: 'confirm', from: 'accepted', to: 'pending-payment', actors: ['taker'] },
{ name: 'accept', from: 'pending-acceptance', to: 'validated', actors: ['owner'] },
{ name: 'pay', from: 'pending-payment', to: 'validated', actors: ['taker'] },
{ name: 'complete', from: 'validated', to: 'completed' },
{ name: 'cancel', from: '*', to: 'cancelled' }
]
}
corresponding to the following flow chart:
For instance, transaction.status
is validated
once Transaction is confirmed by the taker (and paid if required by non-zero price)
and accepted by the owner, making the Asset unavailable with default Asset Type unavailableWhen
property.
# Cancelling a Transaction
To cancel a transaction, some data.cancellationReason
needs to be passed:
await stelace.transactions.createTransition(transaction.id, {
name: 'cancel',
data: { cancellationReason: 'expired' }
})
For readability reasons, only "validated"
status is linked to "cancelled"
final status in the flow chart above, but any Transaction status can be cancelled except for "completed"
status which is a final status too.
# Transaction Events and Workflows
You can customize your Transaction process to the next level with Stelace Workflows.
As a simple example, let’s imagine we want to increment Asset price by 1 when a related Transaction status is changed to "booked"
custom status name.
We just have to create the following Workflow that is triggered when transaction__status_changed
Event is emitted.
TIP
transaction__status_changed
Event is only emitted when you manually update some Transaction status.
await stelace.workflows.create({
name: 'incrementPriceAfterBooking',
event: 'transaction__status_changed', // event triggering this workflow
computed: {
// Evaluated Javascript expression values can be used in all run steps
transactionId: 'transaction.id', // Transaction object is exposed
// "asset", "owner" and "taker" related objects are also available
newPrice: 'asset.price + 1'
},
run: [
{
endpointMethod: 'PATCH', // endpoint verb
// only run this and any following step if transaction.status is "booked"
stop: 'transaction.status !== "booked"',
endpointUri: '/assets/${asset.id}',
endpointPayload: {
price: 'computed.newPrice'
}
}
]
})
To trigger this Workflow, you can manually update any Asset Transaction status to "booked"
, provided your API Key is granted
transaction:config:all
permission API to bypass Asset Type transactionProcess
transitions.
await stelace.transactions.update(transaction.id, {
status: 'booked'
})
Bravo! You’ve just designed your own Transaction process with Workflows.