Rebasely Beta: data may be purged

examples/storefront Public

TypeScript · 81 lines · 4.3 KiB · at 39ab077

import assert from "node:assert/strict";import { test } from "node:test";import Stripe from "stripe";import { cancelOrder, markPaid } from "../src/orders.ts";import { handleWebhook, type Env } from "../workers/stripe-webhook/src/webhook.ts";import { NOW, setup } from "./helpers/app.ts";const SECRET = "whsec_test_secret";const CHECKOUT = { email: "ada@park.test", ship_to: { name: "Ada Park", line1: "12 Alder St", city: "Portland", postal_code: "97205", country: "US" } };// A paid-for order waiting for its webhook, and the webhook Worker wired to the same database// the way the ORDERS service binding wires it in production.async function pendingOrder() {  const s = setup();  await s.addProduct("wool-socks", { price_cents: 1800, stock: 3 });  const cartId = (await s.call("POST", "/carts")).body.cart.id;  await s.call("PUT", `/carts/${cartId}/items/wool-socks`, { quantity: 2 });  const order = (await s.call("POST", `/carts/${cartId}/checkout`, CHECKOUT)).body.order;  const env: Env = {    STRIPE_WEBHOOK_SECRET: SECRET,    ORDERS: {      markPaid: (id, intent) => markPaid(s.env.DB, s.events, id, intent, NOW),      cancel: (id) => cancelOrder(s.env.DB, s.events, id, NOW),    },  };  const deliver = async (type: string, intent: Record<string, unknown>, secret = SECRET) => {    const payload = JSON.stringify({ id: `evt_${type}`, object: "event", type, data: { object: { object: "payment_intent", ...intent } } });    const header = await Stripe.webhooks.generateTestHeaderStringAsync({ payload, secret, cryptoProvider: Stripe.createSubtleCryptoProvider() });    const req = new Request("https://hooks.shop.test/stripe/webhook", { method: "POST", body: payload, headers: { "stripe-signature": header } });    return handleWebhook(req, env);  };  const status = async () => (await s.call("GET", `/orders/${order.id}`)).body.order.status;  return { ...s, order, deliver, status };}test("payment_intent.succeeded marks the order paid and announces it", async () => {  const s = await pendingOrder();  const res = await s.deliver("payment_intent.succeeded", { id: `pi_${s.order.id}`, metadata: { order_id: s.order.id } });  assert.equal(res.status, 200);  assert.equal(await s.status(), "paid");  assert.deepEqual(s.events.sent, [{ type: "order.paid", order_id: s.order.id, at: new Date(NOW).toISOString() }]);});test("a repeated delivery changes nothing but sends the event again", async () => {  const s = await pendingOrder();  const intent = { id: `pi_${s.order.id}`, metadata: { order_id: s.order.id } };  await s.deliver("payment_intent.succeeded", intent);  assert.equal((await s.deliver("payment_intent.succeeded", intent)).status, 200);  assert.equal(await s.status(), "paid");  assert.equal(s.events.sent.length, 2, "at least once: fulfilment dedupes by order id");});test("an intent that is not the order's own does not pay it", async () => {  const s = await pendingOrder();  await s.deliver("payment_intent.succeeded", { id: "pi_someone_else", metadata: { order_id: s.order.id } });  assert.equal(await s.status(), "pending_payment");  assert.deepEqual(s.events.sent, []);});test("payment_intent.canceled cancels the order and returns the stock", async () => {  const s = await pendingOrder();  await s.deliver("payment_intent.canceled", { id: `pi_${s.order.id}`, metadata: { order_id: s.order.id } });  assert.equal(await s.status(), "cancelled");  assert.equal((await s.call("GET", "/products/wool-socks")).body.product.stock, 3);  await s.deliver("payment_intent.canceled", { id: `pi_${s.order.id}`, metadata: { order_id: s.order.id } });  assert.equal((await s.call("GET", "/products/wool-socks")).body.product.stock, 3, "only once");  assert.deepEqual(s.events.sent.map((e) => e.type), ["order.cancelled"]);});test("refuses a payload that Stripe did not sign", async () => {  const s = await pendingOrder();  const res = await s.deliver("payment_intent.succeeded", { id: `pi_${s.order.id}`, metadata: { order_id: s.order.id } }, "whsec_wrong");  assert.equal(res.status, 400);  assert.equal(await s.status(), "pending_payment");});test("acknowledges events it does not handle", async () => {  const s = await pendingOrder();  assert.equal((await s.deliver("payment_intent.created", { id: "pi_1", metadata: {} })).status, 200);  assert.equal((await s.deliver("payment_intent.succeeded", { id: "pi_2", metadata: {} })).status, 200);});