feat(tip): add personalized payment success experience

This commit is contained in:
2026-07-20 19:06:42 +08:00
parent c187f0b817
commit edf50e9cc4
16 changed files with 922 additions and 74 deletions
@@ -65,6 +65,8 @@ describe("payment order flow", () => {
expect(actor.getSnapshot().context).toMatchObject({
currentOrderId: "pay_test_001",
orderStatus: "paid",
tipCount: null,
thankYouMessage: null,
orderPollingStartedAt: null,
launchNonce: 1,
});
@@ -79,6 +81,80 @@ describe("payment order flow", () => {
actor.stop();
});
it("stores a stable Tip success result and clears it on reset", async () => {
const actor = createActor(
createTestPaymentMachine({
orderType: "tip",
orderPlanId: "tip_coffee_usd_9_99",
tipCount: 2,
thankYouMessage: "You made my day.",
}),
).start();
await initialize(actor);
actor.send({
type: "PaymentCreateOrderSubmitted",
recipientCharacterId: "maya-tan",
});
await waitFor(actor, (snapshot) => snapshot.matches("paid"));
expect(actor.getSnapshot().context).toMatchObject({
orderStatus: "paid",
tipCount: 2,
thankYouMessage: "You made my day.",
});
actor.send({ type: "PaymentReset" });
await waitFor(actor, (snapshot) => snapshot.matches("ready"));
expect(actor.getSnapshot().context).toMatchObject({
tipCount: null,
thankYouMessage: null,
});
actor.stop();
});
it("clears the previous Tip result before creating another order", async () => {
const actor = createActor(
createTestPaymentMachine({
orderType: "tip",
tipCount: 3,
thankYouMessage: "Another warm coffee.",
}),
).start();
await initialize(actor);
actor.send({ type: "PaymentCreateOrderSubmitted" });
await waitFor(actor, (snapshot) => snapshot.matches("paid"));
actor.send({ type: "PaymentCreateOrderSubmitted" });
expect(actor.getSnapshot().matches("creatingOrder")).toBe(true);
expect(actor.getSnapshot().context).toMatchObject({
tipCount: null,
thankYouMessage: null,
});
actor.stop();
});
it("clears the previous Tip result when the payment channel changes", async () => {
const actor = createActor(
createTestPaymentMachine({
orderType: "tip",
tipCount: 4,
thankYouMessage: "That was so thoughtful.",
}),
).start();
await initialize(actor);
actor.send({ type: "PaymentCreateOrderSubmitted" });
await waitFor(actor, (snapshot) => snapshot.matches("paid"));
actor.send({ type: "PaymentPayChannelChanged", payChannel: "ezpay" });
expect(actor.getSnapshot().matches("ready")).toBe(true);
expect(actor.getSnapshot().context).toMatchObject({
payChannel: "ezpay",
tipCount: null,
thankYouMessage: null,
});
actor.stop();
});
it("passes the selected tip recipient to order creation", async () => {
const createOrderSpy = vi.fn<CreateOrderSpy>();
const actor = createActor(
@@ -135,6 +211,8 @@ describe("payment order flow", () => {
currentOrderId: null,
payParams: null,
orderStatus: null,
tipCount: null,
thankYouMessage: null,
orderPollingStartedAt: null,
errorMessage: null,
});