An inline Paddle checkout includes three distinct steps:
- Collect user's email and physical address
- Proceed to payment by adding card details
- Confirmation
@paddle/paddle-js
allows listening to events at each step of the checkout flow. By using xstate
we are going to keep track of the current checkout state and update the UI accordingly.
The first step is loading the correct product and its priceId
from the API. We implement this step in a loadProductActor
that accepts the product's slug
as input:
const loadProductActor = fromPromise(
({
input: { slug },
}: {
input: {
slug: string;
};
}) =>
RuntimeClient.runPromise(
Effect.gen(function* () {
const api = yield* Api;
// 👇 Type-safe HTTP client using `Api` service
return yield* api.paddle.product({ path: { slug } });
})
)
);
If we find a valid product, the next step is using the Paddle
service to initialize the checkout. We implement this step in another paddleInitActor
:
const paddleInitActor = fromPromise(
({
input: { clientToken },
}: {
input: {
clientToken: string;
};
}) =>
RuntimeClient.runPromise(
Effect.gen(function* () {
// 👇 Use `Paddle` service to initialize the checkout (`initializePaddle`)
const _ = yield* Paddle;
return yield* _({ clientToken });
})
)
);
The returned Paddle
client is stored inside the machine's context. Using the client and the priceId
from the API, we call Checkout.open
to initialize the checkout and Update
to listen to callback events:
Checkout.open
instructs Paddle to start the checkout flowUpdate
allows to provide a neweventCallback
function that will be called each time a checkout event is triggered
entry: [
({ context, self }) =>
context.paddle?.Update({
eventCallback: (event) => {
if (event.name === CheckoutEventNames.CHECKOUT_CUSTOMER_CREATED) {
self.send({ type: "checkout.created" });
} else if (event.name === CheckoutEventNames.CHECKOUT_COMPLETED) {
self.send({ type: "checkout.completed" });
}
},
}),
({ context }) =>
context.paddle?.Checkout.open({
items: [{ priceId: context.price?.id!, quantity: 1 }],
}),
]
With this implementation all the steps during the checkout are synced with the state machine. Later inside the component we will display the current checkout state to the user.