-
-
Save SariSaar/011a387db6a4555c7c4d0f1f70974a9b to your computer and use it in GitHub Desktop.
Code examples for building a shopping cart in Sharetribe Web Template – Designing a transaction flow
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
{:format :v3, | |
:transitions | |
[{:name :transition/reserve-stock, | |
:actor :actor.role/customer, | |
:actions | |
[{:name :action/update-protected-data} | |
{:name :action/create-pending-stock-reservation}], | |
:to :state/pending-stock} | |
{:name :transition/confirm-stock, | |
:actor :actor.role/customer | |
:actions | |
[{:name :action/accept-stock-reservation}] | |
:from :state/pending-stock | |
:to :state/purchased} | |
{:name :transition/auto-expire-stock, | |
:actions [{:name :action/decline-stock-reservation}] | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/pending-stock]} | |
{:fn/period ["PT15M"]}]}, | |
:from :state/pending-stock | |
:to :state/canceled} | |
{:name :transition/cancel-stock, | |
:actor :actor.role/operator, | |
:actions | |
[{:name :action/cancel-stock-reservation}], | |
:from :state/purchased, | |
:to :state/canceled} | |
{:name :transition/complete-stock, | |
:actor :actor.role/operator | |
:actions [] | |
:from :state/purchased | |
:to :state/completed} | |
{:name :transition/auto-complete-stock, | |
:actions [] | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/purchased]} | |
{:fn/period ["P60D"]}]}, | |
:from :state/purchased | |
:to :state/completed} | |
] | |
:notifications | |
[]} |
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
;; Updates to default-purchase process for handling cart transactions | |
{:format :v3, | |
:transitions | |
[{:name :transition/inquire, | |
:actor :actor.role/customer, | |
:actions [{:name :action/update-protected-data}], | |
:to :state/inquiry} | |
{:name :transition/request-payment, | |
:actor :actor.role/customer, | |
:privileged? true, | |
:actions | |
[{:name :action/update-protected-data} | |
{:name :action/privileged-set-line-items} | |
{:name :action/stripe-create-payment-intent}], | |
:to :state/pending-update-child-transactions} | |
{:name :transition/request-payment-after-inquiry, | |
:actor :actor.role/customer, | |
:privileged? true, | |
:actions | |
[{:name :action/update-protected-data} | |
{:name :action/privileged-set-line-items} | |
{:name :action/stripe-create-payment-intent}], | |
:from :state/inquiry, | |
:to :state/pending-update-child-transactions} | |
{:name :transition/update-child-transactions, | |
:actor :actor.role/customer, | |
:actions | |
[{:name :action/update-protected-data}], | |
:from :state/pending-update-child-transactions | |
:to :state/pending-payment} | |
{:name :transition/confirm-payment, | |
:actor :actor.role/customer, | |
:actions | |
[{:name :action/stripe-confirm-payment-intent} | |
{:name :action/stripe-capture-payment-intent}], | |
:from :state/pending-payment, | |
:to :state/purchased} | |
{:name :transition/expire-payment, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/pending-payment]} | |
{:fn/period ["PT15M"]}]}, | |
:actions | |
[{:name :action/calculate-full-refund} | |
{:name :action/stripe-refund-payment}], | |
:from :state/pending-payment, | |
:to :state/payment-expired} | |
{:name :transition/mark-received-from-purchased, | |
:actor :actor.role/customer, | |
:actions [{:name :action/stripe-create-payout}], | |
:from :state/purchased, | |
:to :state/received} | |
{:name :transition/mark-delivered, | |
:actor :actor.role/provider, | |
:actions [], | |
:from :state/purchased, | |
:to :state/delivered} | |
{:name :transition/operator-mark-delivered, | |
:actor :actor.role/operator, | |
:actions [], | |
:from :state/purchased, | |
:to :state/delivered} | |
{:name :transition/mark-received, | |
:actor :actor.role/customer, | |
:actions [{:name :action/stripe-create-payout}], | |
:from :state/delivered, | |
:to :state/received} | |
{:name :transition/auto-mark-received, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/delivered]} | |
{:fn/period ["P14D"]}]}, | |
:actions [{:name :action/stripe-create-payout}], | |
:from :state/delivered, | |
:to :state/received} | |
{:name :transition/dispute, | |
:actor :actor.role/customer, | |
:actions [{:name :action/update-protected-data}], | |
:from :state/delivered, | |
:to :state/disputed} | |
{:name :transition/operator-dispute, | |
:actor :actor.role/operator, | |
:actions [], | |
:from :state/delivered, | |
:to :state/disputed} | |
{:name :transition/mark-received-from-disputed, | |
:actor :actor.role/operator, | |
:actions [{:name :action/stripe-create-payout}], | |
:from :state/disputed, | |
:to :state/received} | |
{:name :transition/cancel, | |
:actor :actor.role/operator, | |
:actions | |
[{:name :action/calculate-full-refund} | |
{:name :action/stripe-refund-payment}], | |
:from :state/purchased, | |
:to :state/canceled} | |
{:name :transition/auto-cancel, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/purchased]} | |
{:fn/period ["P14D"]}]}, | |
:actions | |
[{:name :action/calculate-full-refund} | |
{:name :action/stripe-refund-payment}], | |
:from :state/purchased, | |
:to :state/canceled} | |
{:name :transition/operator-pending-partial-refund-from-disputed | |
:actor :actor.role/operator | |
:actions [] | |
:from :state/disputed | |
:to :state/pending-partial-refund} | |
{:name :transition/operator-mark-received-with-partial-refund | |
:actor :actor.role/operator | |
:actions [] | |
:from :state/pending-partial-refund | |
:to :state/received} | |
{:name :transition/cancel-from-disputed, | |
:actor :actor.role/operator, | |
:actions | |
[{:name :action/calculate-full-refund} | |
{:name :action/stripe-refund-payment}], | |
:from :state/disputed, | |
:to :state/canceled} | |
{:name :transition/auto-cancel-from-disputed, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/disputed]} | |
{:fn/period ["P60D"]}]}, | |
:actions | |
[{:name :action/calculate-full-refund} | |
{:name :action/stripe-refund-payment}], | |
:from :state/disputed, | |
:to :state/canceled} | |
{:name :transition/auto-complete, | |
:at {:fn/timepoint [:time/first-entered-state :state/received]}, | |
:actions [], | |
:from :state/received, | |
:to :state/completed} | |
{:name :transition/review-1-by-provider, | |
:actor :actor.role/provider, | |
:actions [{:name :action/post-review-by-provider}], | |
:from :state/completed, | |
:to :state/reviewed-by-provider} | |
{:name :transition/review-2-by-provider, | |
:actor :actor.role/provider, | |
:actions | |
[{:name :action/post-review-by-provider} | |
{:name :action/publish-reviews}], | |
:from :state/reviewed-by-customer, | |
:to :state/reviewed} | |
{:name :transition/review-1-by-customer, | |
:actor :actor.role/customer, | |
:actions [{:name :action/post-review-by-customer}], | |
:from :state/completed, | |
:to :state/reviewed-by-customer} | |
{:name :transition/review-2-by-customer, | |
:actor :actor.role/customer, | |
:actions | |
[{:name :action/post-review-by-customer} | |
{:name :action/publish-reviews}], | |
:from :state/reviewed-by-provider, | |
:to :state/reviewed} | |
{:name :transition/expire-review-period, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/received]} | |
{:fn/period ["P7D"]}]}, | |
:actions [], | |
:from :state/completed, | |
:to :state/reviewed} | |
{:name :transition/expire-provider-review-period, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/received]} | |
{:fn/period ["P7D"]}]}, | |
:actions [{:name :action/publish-reviews}], | |
:from :state/reviewed-by-customer, | |
:to :state/reviewed} | |
{:name :transition/expire-customer-review-period, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/received]} | |
{:fn/period ["P7D"]}]}, | |
:actions [{:name :action/publish-reviews}], | |
:from :state/reviewed-by-provider, | |
:to :state/reviewed}], | |
:notifications | |
[{:name :notification/order-receipt, | |
:on :transition/confirm-payment, | |
;; This notification is delayed to give the customer a chance to verify their | |
;; email address, in case they are a new customer. | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/purchased]} | |
{:fn/period ["PT15M"]}]}, | |
:to :actor.role/customer, | |
:template :purchase-order-receipt} | |
{:name :notification/purchase-new-order, | |
:on :transition/confirm-payment, | |
:to :actor.role/provider, | |
:template :purchase-new-order} | |
{:name :notification/shipping-reminder, | |
:on :transition/confirm-payment, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/purchased]} | |
{:fn/period ["P3D"]}]}, | |
:to :actor.role/provider, | |
:template :purchase-shipping-reminder} | |
{:name :notification/order-marked-as-delivered, | |
:on :transition/mark-delivered, | |
:to :actor.role/customer, | |
:template :purchase-order-marked-as-delivered} | |
{:name | |
:notification/purchase-order-operator-marked-as-delivered-to-customer, | |
:on :transition/operator-mark-delivered, | |
:to :actor.role/customer, | |
:template :purchase-order-marked-as-delivered} | |
{:name | |
:notification/purchase-order-operator-marked-as-delivered-to-provider, | |
:on :transition/operator-mark-delivered, | |
:to :actor.role/provider, | |
:template :purchase-order-operator-marked-as-delivered} | |
{:name :notification/purchase-mark-order-received-reminder, | |
:on :transition/mark-delivered, | |
:at | |
{:fn/plus | |
[{:fn/timepoint [:time/first-entered-state :state/delivered]} | |
{:fn/period ["P12D"]}]}, | |
:to :actor.role/customer, | |
:template :purchase-mark-order-received-reminder} | |
{:name :notification/order-marked-as-received-from-purchased, | |
:on :transition/mark-received-from-purchased, | |
:to :actor.role/provider, | |
:template :purchase-order-marked-as-received} | |
{:name :notification/order-marked-as-received, | |
:on :transition/mark-received, | |
:to :actor.role/provider, | |
:template :purchase-order-marked-as-received} | |
{:name :notification/shipping-time-expired, | |
:on :transition/auto-cancel, | |
:to :actor.role/customer, | |
:template :purchase-shipping-time-expired-customer} | |
{:name :notification/order-shipping-time-expired, | |
:on :transition/auto-cancel, | |
:to :actor.role/provider, | |
:template :purchase-shipping-time-expired-provider} | |
{:name :notification/purchase-canceled, | |
:on :transition/cancel, | |
:to :actor.role/customer, | |
:template :purchase-order-canceled-to-customer} | |
{:name :notification/order-canceled, | |
:on :transition/cancel, | |
:to :actor.role/provider, | |
:template :purchase-order-canceled-to-provider} | |
{:name :notification/purchase-order-auto-marked-as-received-customer, | |
:on :transition/auto-mark-received, | |
:to :actor.role/customer, | |
:template :purchase-order-auto-marked-as-received-customer} | |
{:name :notification/purchase-order-auto-marked-as-received-provider, | |
:on :transition/auto-mark-received, | |
:to :actor.role/provider, | |
:template :purchase-order-auto-marked-as-received-provider} | |
{:name :notification/order-disputed, | |
:on :transition/dispute, | |
:to :actor.role/provider, | |
:template :purchase-order-disputed} | |
{:name :notification/purchase-order-operator-disputed-to-customer, | |
:on :transition/operator-dispute, | |
:to :actor.role/customer, | |
:template :purchase-order-operator-disputed} | |
{:name :notification/purchase-order-operator-disputed-to-provider, | |
:on :transition/operator-dispute, | |
:to :actor.role/provider, | |
:template :purchase-order-disputed} | |
{:name :notification/order-received-from-disputed-customer, | |
:on :transition/mark-received-from-disputed, | |
:to :actor.role/customer, | |
:template :purchase-order-received-from-disputed-customer} | |
{:name :notification/order-received-from-disputed-provider, | |
:on :transition/mark-received-from-disputed, | |
:to :actor.role/provider, | |
:template :purchase-order-received-from-disputed-provider} | |
{:name :notification/order-received-from-disputed-partial-refund-customer, | |
:on :transition/operator-mark-received-with-partial-refund, | |
:to :actor.role/customer, | |
:template :purchase-order-received-from-disputed-customer} | |
{:name :notification/order-received-from-disputed-partial-refund-provider, | |
:on :transition/operator-mark-received-with-partial-refund, | |
:to :actor.role/provider, | |
:template :purchase-order-received-from-disputed-provider} | |
{:name :notification/canceled-from-disputed-customer, | |
:on :transition/cancel-from-disputed, | |
:to :actor.role/customer, | |
:template :purchase-order-canceled-from-disputed-customer} | |
{:name :notification/canceled-from-disputed-provider, | |
:on :transition/cancel-from-disputed, | |
:to :actor.role/provider, | |
:template :purchase-order-canceled-from-disputed-provider} | |
{:name :notification/auto-canceled-from-disputed-customer, | |
:on :transition/auto-cancel-from-disputed, | |
:to :actor.role/customer, | |
:template :purchase-order-auto-canceled-from-disputed-customer} | |
{:name :notification/auto-canceled-from-disputed-provider, | |
:on :transition/auto-cancel-from-disputed, | |
:to :actor.role/provider, | |
:template :purchase-order-auto-canceled-from-disputed-provider} | |
{:name :notification/review-period-start-provider, | |
:on :transition/auto-complete, | |
:to :actor.role/provider, | |
:template :purchase-order-review-by-provider-wanted} | |
{:name :notification/review-period-start-customer, | |
:on :transition/auto-complete, | |
:to :actor.role/customer, | |
:template :purchase-order-review-by-customer-wanted} | |
{:name :notification/review-by-provider-first, | |
:on :transition/review-1-by-provider, | |
:to :actor.role/customer, | |
:template :purchase-review-by-other-party-unpublished} | |
{:name :notification/review-by-customer-first, | |
:on :transition/review-1-by-customer, | |
:to :actor.role/provider, | |
:template :purchase-review-by-other-party-unpublished} | |
{:name :notification/review-by-provider-second, | |
:on :transition/review-2-by-provider, | |
:to :actor.role/customer, | |
:template :purchase-review-by-other-party-published} | |
{:name :notification/review-by-customer-second, | |
:on :transition/review-2-by-customer, | |
:to :actor.role/provider, | |
:template :purchase-review-by-other-party-published}]} |
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
/** | |
* Transaction process graph for product order cart items: | |
* - cart-stock-process | |
*/ | |
/** | |
* Transitions | |
* | |
* These strings must sync with values defined in Marketplace API, | |
* since transaction objects given by API contain info about last transitions. | |
* All the actions in API side happen in transitions, | |
* so we need to understand what those strings mean. | |
*/ | |
export const transitions = { | |
CART_TRANSITION_RESERVE_STOCK: 'transition/reserve-stock', | |
CART_TRANSITION_CONFIRM_STOCK: 'transition/confirm-stock', | |
CART_TRANSITION_AUTO_EXPIRE_STOCK: 'transition/auto-expire-stock', | |
CART_TRANSITION_CANCEL_STOCK: 'transition/cancel-stock', | |
CART_TRANSITION_COMPLETE_STOCK: 'transition/complete-stock', | |
CART_TRANSITION_AUTO_COMPLETE_STOCK: 'transition/auto-complete-stock', | |
}; | |
/** | |
* States | |
* | |
* These constants are only for making it clear how transitions work together. | |
* You should not use these constants outside of this file. | |
* | |
* Note: these states are not in sync with states used transaction process definitions | |
* in Marketplace API. Only last transitions are passed along transaction object. | |
*/ | |
export const states = { | |
INITIAL: 'initial', | |
PENDING_STOCK: 'pending-stock', | |
PURCHASED: 'purchased', | |
CANCELED: 'canceled', | |
COMPLETED: 'completed', | |
}; | |
/** | |
* Description of transaction process graph | |
* | |
* You should keep this in sync with transaction process defined in Marketplace API | |
* | |
* Note: we don't use yet any state machine library, | |
* but this description format is following Xstate (FSM library) | |
* https://xstate.js.org/docs/ | |
*/ | |
export const graph = { | |
// id is defined only to support Xstate format. | |
// However if you have multiple transaction processes defined, | |
// it is best to keep them in sync with transaction process aliases. | |
id: 'cart-stock-process/release-1', | |
// This 'initial' state is a starting point for new transaction | |
initial: states.INITIAL, | |
// States | |
states: { | |
[states.INITIAL]: { | |
on: { | |
[transitions.CART_TRANSITION_RESERVE_STOCK]: states.PENDING_STOCK, | |
}, | |
}, | |
[states.PENDING_STOCK]: { | |
on: { | |
[transitions.CART_TRANSITION_CONFIRM_STOCK]: states.PURCHASED, | |
[transitions.CART_TRANSITION_AUTO_EXPIRE_STOCK]: states.CANCELED, | |
}, | |
}, | |
[states.PURCHASED]: { | |
on: { | |
[transitions.CART_TRANSITION_CANCEL_STOCK]: states.CANCELED, | |
[transitions.CART_TRANSITION_COMPLETE_STOCK]: states.COMPLETED, | |
[transitions.CART_TRANSITION_AUTO_COMPLETE_STOCK]: states.COMPLETED, | |
}, | |
}, | |
[states.CANCELED]: { type: 'final' }, | |
[states.COMPLETED]: { type: 'final' }, | |
}, | |
}; |
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
... | |
export const transitions = { | |
... | |
UPDATE_CHILD_TRANSACTIONS: 'transition/update-child-transactions', | |
... | |
OPERATOR_PENDING_PARTIAL_REFUND_FROM_DISPUTED: | |
'transition/operator-pending-partial-refund-from-disputed', | |
OPERATOR_MARK_RECEIVED_WITH_PARTIAL_REFUND: | |
'transition/operator-mark-received-with-partial-refund', | |
... | |
}; | |
/** | |
* States | |
* | |
* These constants are only for making it clear how transitions work together. | |
* You should not use these constants outside of this file. | |
* | |
* Note: these states are not in sync with states used transaction process definitions | |
* in Marketplace API. Only last transitions are passed along transaction object. | |
*/ | |
export const states = { | |
... | |
PENDING_UPDATE_CHILD_TRANSACTIONS: 'pending-update-child-transactions', | |
... | |
PENDING_PARTIAL_REFUND: 'pending-partial-refund', | |
... | |
}; | |
/** | |
* Description of transaction process graph | |
* | |
* You should keep this in sync with transaction process defined in Marketplace API | |
* | |
* Note: we don't use yet any state machine library, | |
* but this description format is following Xstate (FSM library) | |
* https://xstate.js.org/docs/ | |
*/ | |
export const graph = { | |
// id is defined only to support Xstate format. | |
// However if you have multiple transaction processes defined, | |
// it is best to keep them in sync with transaction process aliases. | |
id: 'default-purchase/release-1', | |
// This 'initial' state is a starting point for new transaction | |
initial: states.INITIAL, | |
// States | |
states: { | |
[states.INITIAL]: { | |
on: { | |
[transitions.INQUIRE]: states.INQUIRY, | |
[transitions.REQUEST_PAYMENT]: states.PENDING_UPDATE_CHILD_TRANSACTIONS, | |
}, | |
}, | |
[states.INQUIRY]: { | |
on: { | |
[transitions.REQUEST_PAYMENT_AFTER_INQUIRY]: states.PENDING_UPDATE_CHILD_TRANSACTIONS, | |
}, | |
}, | |
[states.PENDING_UPDATE_CHILD_TRANSACTIONS]: { | |
on: { | |
[transitions.UPDATE_CHILD_TRANSACTIONS]: states.PENDING_PAYMENT, | |
}, | |
}, | |
... | |
[states.DISPUTED]: { | |
on: { | |
[transitions.AUTO_CANCEL_FROM_DISPUTED]: states.CANCELED, | |
[transitions.CANCEL_FROM_DISPUTED]: states.CANCELED, | |
[transitions.MARK_RECEIVED_FROM_DISPUTED]: states.RECEIVED, | |
[transitions.OPERATOR_PENDING_PARTIAL_REFUND_FROM_DISPUTED]: states.PENDING_PARTIAL_REFUND, | |
}, | |
}, | |
[states.PENDING_PARTIAL_REFUND]: { | |
on: { | |
[transitions.OPERATOR_MARK_RECEIVED_WITH_PARTIAL_REFUND]: states.RECEIVED, | |
}, | |
}, | |
... | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment