for japanese: README.ja.md
orderable.ts is an npm library that works with Cloud Functions for Firebase and can easily execute payment.
EC requires a lot of processing. For example, check number of stocks, payment process, create history, and so on. orderable.ts exec these troublesome tasks.
For Client Side (iOS): starhoshi/Orderable
- Required
- Yarn
- If use npm, the dependency can not be resolved and an error will result.
- TypeScript
- Necessary to define the model.
- Yarn
yarn add @star__hoshi/orderable pring
yarn add typescript --dev
Sample function is orderable.ts/index.ts. Please refer to it.
orderable.ts depends on 1amageek/pring.ts. So, set experimentalDecorators
to true
.
{
"compilerOptions": {
"target": "es2017",
"lib": ["es2017"],
"module": "commonjs",
"experimentalDecorators": true,
"sourceMap": true
}
}
Initialize orderable.ts and pring.ts in your index.ts.
import * as Orderable from '@star__hoshi/orderable'
import { Pring } from 'pring'
Pring.initialize(functions.config().firebase)
Orderable.initialize({
adminOptions: functions.config().firebase,
stripeToken: 'YOUR_STRIPE_TOKEN_IF_NEEDED',
slack: undefined // OR {url: "YOUR_SLACK_URL", channel: 'CHANNEL_NAME'}
})
You need to define the necessary Model in your project.
Required interface is here, sample model definition is here.
- User
- Buyer
- Shop
- Seller
- Product
- Product concept.
- SKU
- Entity of the product. Have inventory and price etc.
- Order
- Order have payment amount and payment method etc.
- OrderShop
- Order information for each shop.
- OrderSKU
- The item ordered. Have quantity etc.
Initialize orderObject and execute orderPaymentRequested as follows.
export const paySampleOrder = functions.firestore
.document(`${Model.SampleOrder.getPath()}/{orderID}`)
.onUpdate(async (event) => {
const orderObject = new Orderable.Functions.OrderObject<Model.SampleOrder, Model.SampleShop, Model.SampleUser, Model.SampleSKU, Model.SampleProduct, Model.SampleOrderShop, Model.SampleOrderSKU>(event, {
order: Model.SampleOrder,
shop: Model.SampleShop,
user: Model.SampleUser,
sku: Model.SampleSKU,
product: Model.SampleProduct,
orderShop: Model.SampleOrderShop,
orderSKU: Model.SampleOrderSKU
})
try {
Orderable.Functions.orderPaymentRequested(orderObject)
} catch (e) {
console.error(e)
}
})
deploy:
firebase deploy --only functions:paySampleOrder
Orderable.Functions.orderPaymentRequested(orderObject)
will start when order.paymentStatus becomes PaymentRequested.
order.paymentStatus = Orderable.Model.OrderPaymentStatus.PaymentRequested
await order.update()
When purchase processing is completed, order.neoTask.status === 1
will be set. That is a sign of success.
if (order.neoTask && order.neoTask.status === 1) {
// payment completed
}
Also, the status of Order and OrderShop will be changed as follows.
- Order
- paymentStatus === 4 (Orderable.OrderPaymentStatus.Paid)
- OrderShop[]
- paymentStatus === 3 (Orderable.OrderShopPaymentStatus.Paid)
If the function fails, order.neoTask.status === 2
will be set. And detailed error data will be set in neoTask.
This error occurs when you need to modify order information.For example, when the credit card is invalid, out of stock, etc.
You must change your credit card, change skus you order, etc.
export enum ValidationErrorType {
ShopIsNotActive = 'ShopIsNotActive',
SKUIsNotActive = 'SKUIsNotActive',
OutOfStock = 'OutOfStock',
StripeCardError = 'StripeCardError',
StripeInvalidRequestError = 'StripeInvalidRequestError',
StripeCardExpired = 'StripeCardExpired',
PaymentInfoNotFound = 'PaymentInfoNotFound'
}
Please set the necessary conditions again and execute the trigger of 4. Start payment
again. In some cases it may be better to recreate the Order.
This error will be set when an unrecoverable problem occurs. For example, when saving payment completion data failed, etc.
In this case retry can not solve it, so you have to check and correct the data directly.
This state can be solved by re-running Cloud Functions. In this case, Orderable automatically retries the function.
If retry can not be solved twice, a fatal error will be set. If it succeeds, neoTask.status === 1
.
As soon as it changes to fatal or success, please wait for it.
If processing fails, you can catch an Error of type FlowError. FlowError has an error, you can handle error handling as well.
try {
Orderable.Functions.orderPaymentRequested(event, orderObject)
} catch (e) {
console.error(e)
if (e.constructor === Orderable.FlowError) {
console.log(e.step)
if (e.error.constructor === Orderable.StripeError) {
// post to slack ...
}
}
}
Note: Trigger events are delivered at least once, which means that rarely, spurious duplicates may occur. https://cloud.google.com/functions/docs/concepts/events-triggers#triggers
Cloud Functions rarely fire multiple times.
The stock will be reduced more, and multiple payout will occur.
We use transactions to prevent it. Once Orderable is started, save the flag in neoTask.completed
. And if the flag is already true, Orderable will stop.
https://github.com/starhoshi/orderable.ts/blob/master/orderable/src/orderable.ts#L433-L434