@develemit/commerce-server 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.9.0 (2026-11-14)
2
+
3
+ ### Features
4
+
5
+ - Products can carry an optional Stripe `taxCode`. Migration `0011` adds `commerce_products.tax_code`; the admin API reads and writes it, checkout sends it on that line (`CheckoutLine.taxCode`, which the Stripe gateway passes as `tax_code`), and the public catalog does not expose it.
6
+ - `orderEmailLines` adds a `tax` line (structured, text and HTML, label `labels.tax`, default "Tax") between shipping and total, only when the order's `taxCents` is above 0.
7
+ - Opt-in automatic sales tax through Stripe Tax: `createStripeGateway({ tax: { enabled: true, behavior?, shippingTaxCode? } })` (or `resolvePayment(env, { tax })`) sets `automatic_tax`, a `tax_behavior` on every line and shipping option, and a per-line `taxCode` (`CheckoutLine.taxCode`) when present. The completed-session webhook reads `total_details.amount_tax` into `taxCents`. Without the option, sessions are unchanged. The fake gateway accepts `tax: { rate }` and `taxFor()`, and `devCheckout` records the computed tax.
8
+ - Orders record sales tax. Migration `0010` adds `commerce_orders.tax_cents` (default 0). A `checkout.completed` `PaymentEvent` may carry `taxCents`; it is stored on the order and included in `totalCents`, so full refunds return the tax. Public and admin order responses return `taxCents`. The fake gateway's `completed()` accepts `taxCents`.
9
+
1
10
  ## 0.8.0 (2026-11-12)
2
11
 
3
12
  ### Features
package/README.md CHANGED
@@ -136,7 +136,8 @@ onOrderPaid: async (order) => {
136
136
  `formatOrderRef` in contracts); print it instead of the full id.
137
137
 
138
138
  The result also has structured `items`, `subtotal`, `discount` (absent when
139
- no discount applied), `shipping`, `total` and `address` (absent for pickup).
139
+ no discount applied), `shipping`, `tax` (absent when the order has no tax), `total` and `address`
140
+ (absent for pickup).
140
141
  HTML output is `<tr>` / `<p>` fragments with `& < > " '` escaped. Override
141
142
  copy with `labels`.
142
143
 
package/dist/index.d.ts CHANGED
@@ -214,6 +214,23 @@ declare const commerceSchema: {
214
214
  identity: undefined;
215
215
  generated: undefined;
216
216
  }, {}, {}>;
217
+ taxCode: drizzle_orm_pg_core.PgColumn<{
218
+ name: "tax_code";
219
+ tableName: "commerce_products";
220
+ dataType: "string";
221
+ columnType: "PgText";
222
+ data: string;
223
+ driverParam: string;
224
+ notNull: false;
225
+ hasDefault: false;
226
+ isPrimaryKey: false;
227
+ isAutoincrement: false;
228
+ hasRuntimeDefault: false;
229
+ enumValues: [string, ...string[]];
230
+ baseColumn: never;
231
+ identity: undefined;
232
+ generated: undefined;
233
+ }, {}, {}>;
217
234
  publishedAt: drizzle_orm_pg_core.PgColumn<{
218
235
  name: "published_at";
219
236
  tableName: "commerce_products";
@@ -645,6 +662,23 @@ declare const commerceSchema: {
645
662
  identity: undefined;
646
663
  generated: undefined;
647
664
  }, {}, {}>;
665
+ taxCents: drizzle_orm_pg_core.PgColumn<{
666
+ name: "tax_cents";
667
+ tableName: "commerce_orders";
668
+ dataType: "number";
669
+ columnType: "PgInteger";
670
+ data: number;
671
+ driverParam: string | number;
672
+ notNull: true;
673
+ hasDefault: true;
674
+ isPrimaryKey: false;
675
+ isAutoincrement: false;
676
+ hasRuntimeDefault: false;
677
+ enumValues: undefined;
678
+ baseColumn: never;
679
+ identity: undefined;
680
+ generated: undefined;
681
+ }, {}, {}>;
648
682
  currency: drizzle_orm_pg_core.PgColumn<{
649
683
  name: "currency";
650
684
  tableName: "commerce_orders";
@@ -1686,6 +1720,7 @@ interface CheckoutLine {
1686
1720
  name: string;
1687
1721
  unitAmountCents: number;
1688
1722
  quantity: number;
1723
+ taxCode?: string;
1689
1724
  }
1690
1725
  interface CreateCheckoutSessionInput {
1691
1726
  reference: string;
@@ -1731,6 +1766,7 @@ type PaymentEvent = {
1731
1766
  email?: string;
1732
1767
  shippingAddress?: ShippingAddress;
1733
1768
  shipping?: ChosenShipping;
1769
+ taxCents?: number;
1734
1770
  paymentPending?: boolean;
1735
1771
  } | {
1736
1772
  id: string;
@@ -1844,8 +1880,14 @@ interface StripeGatewayOptions {
1844
1880
  secretKey: string;
1845
1881
  webhookSecret: string;
1846
1882
  client?: Stripe;
1883
+ tax?: StripeTaxOptions;
1884
+ }
1885
+ interface StripeTaxOptions {
1886
+ enabled: true;
1887
+ behavior?: 'exclusive' | 'inclusive';
1888
+ shippingTaxCode?: string;
1847
1889
  }
1848
- declare function createStripeGateway({ secretKey, webhookSecret, client, }: StripeGatewayOptions): PaymentGateway;
1890
+ declare function createStripeGateway({ secretKey, webhookSecret, client, tax, }: StripeGatewayOptions): PaymentGateway;
1849
1891
 
1850
1892
  interface SignedWebhook {
1851
1893
  rawBody: Buffer;
@@ -1868,6 +1910,7 @@ interface FakeGateway extends PaymentGateway {
1868
1910
  email?: string;
1869
1911
  shippingAddress?: ShippingAddress;
1870
1912
  shipping?: ChosenShipping;
1913
+ taxCents?: number;
1871
1914
  paymentPending?: boolean;
1872
1915
  }): SignedWebhook;
1873
1916
  asyncPaid(sessionId: string): SignedWebhook;
@@ -1875,11 +1918,16 @@ interface FakeGateway extends PaymentGateway {
1875
1918
  expired(sessionId: string): SignedWebhook;
1876
1919
  refunded(sessionId: string, refunds: GatewayRefundEvent[]): SignedWebhook;
1877
1920
  sign(event: PaymentEvent): SignedWebhook;
1921
+ taxFor(sessionId: string, shippingCents?: number): number;
1922
+ }
1923
+ interface FakeTaxOptions {
1924
+ rate: number;
1878
1925
  }
1879
1926
  interface FakeGatewayOptions {
1880
1927
  secret?: string;
1928
+ tax?: FakeTaxOptions;
1881
1929
  }
1882
- declare function createFakeGateway({ secret, }?: FakeGatewayOptions): FakeGateway;
1930
+ declare function createFakeGateway({ secret, tax, }?: FakeGatewayOptions): FakeGateway;
1883
1931
  declare function isFakeGateway(gateway: PaymentGateway): gateway is FakeGateway;
1884
1932
 
1885
1933
  interface HoldSweeperOptions {
@@ -1899,6 +1947,7 @@ interface OrderEmailLabels {
1899
1947
  subtotal: string;
1900
1948
  discount: (code: string | null) => string;
1901
1949
  shipping: (label: string | null) => string;
1950
+ tax: string;
1902
1951
  total: string;
1903
1952
  }
1904
1953
  interface OrderEmailLinesOptions {
@@ -1921,6 +1970,7 @@ interface OrderEmailLines {
1921
1970
  subtotal: EmailLine;
1922
1971
  discount?: EmailLine;
1923
1972
  shipping: EmailLine;
1973
+ tax?: EmailLine;
1924
1974
  total: EmailLine;
1925
1975
  address?: string[];
1926
1976
  text: OrderEmailLinesText;
@@ -1986,7 +2036,11 @@ interface PaymentEnv {
1986
2036
  STRIPE_SECRET_KEY?: string;
1987
2037
  STRIPE_WEBHOOK_SECRET?: string;
1988
2038
  }
1989
- declare function resolvePayment(env?: PaymentEnv): PaymentGateway;
2039
+ interface ResolvePaymentOptions {
2040
+ tax?: StripeTaxOptions;
2041
+ fakeTax?: FakeTaxOptions;
2042
+ }
2043
+ declare function resolvePayment(env?: PaymentEnv, { tax, fakeTax }?: ResolvePaymentOptions): PaymentGateway;
1990
2044
  interface CheckoutUrlPaths {
1991
2045
  successPath?: string;
1992
2046
  cancelPath?: string;
@@ -1994,4 +2048,4 @@ interface CheckoutUrlPaths {
1994
2048
  declare function checkoutUrls(webUrl: string, { successPath, cancelPath, }?: CheckoutUrlPaths): CheckoutUrls;
1995
2049
 
1996
2050
  export { ProductNotFoundError, SESSION_ID_PLACEHOLDER, WebhookSignatureError, catalogPlugin, checkoutUrls, commercePlugin, commerceSchema, createDiskStorage, createFakeGateway, createMemoryStorage, createStripeGateway, isFakeGateway, openStoredFile, orderEmailLines, ordersPlugin, resolvePayment, runCommerceMigrations, seedProducts, seedShippingRates, startHoldSweeper };
1997
- export type { AdminOptions, CatalogCacheOptions, CatalogPluginOptions, CategoryLabels, CheckoutDiscount, CheckoutLine, CheckoutSession, CheckoutUrlPaths, CheckoutUrls, ChosenShipping, CommerceDb, CommercePluginOptions, CommerceSchema, CreateCheckoutSessionInput, DevBuyer, DevBuyerSetting, DevCheckoutSetting, DiskStorageOptions, EmailLine, FakeGateway, FakeGatewayOptions, FileStorage, GatewayRefund, HoldSweeper, HoldSweeperOptions, MemoryStorage, OrderEmailLabels, OrderEmailLines, OrderEmailLinesOptions, OrderHooks, OrdersPluginOptions, PaymentEnv, PaymentEvent, PaymentGateway, RefundInput, SeedProduct, SeedShippingRate, ShippingAddress, ShippingRateOption, SignedWebhook, StoredFile, StoredFileStream, StripeGatewayOptions };
2051
+ export type { AdminOptions, CatalogCacheOptions, CatalogPluginOptions, CategoryLabels, CheckoutDiscount, CheckoutLine, CheckoutSession, CheckoutUrlPaths, CheckoutUrls, ChosenShipping, CommerceDb, CommercePluginOptions, CommerceSchema, CreateCheckoutSessionInput, DevBuyer, DevBuyerSetting, DevCheckoutSetting, DiskStorageOptions, EmailLine, FakeGateway, FakeGatewayOptions, FakeTaxOptions, FileStorage, GatewayRefund, HoldSweeper, HoldSweeperOptions, MemoryStorage, OrderEmailLabels, OrderEmailLines, OrderEmailLinesOptions, OrderHooks, OrdersPluginOptions, PaymentEnv, PaymentEvent, PaymentGateway, RefundInput, ResolvePaymentOptions, SeedProduct, SeedShippingRate, ShippingAddress, ShippingRateOption, SignedWebhook, StoredFile, StoredFileStream, StripeGatewayOptions, StripeTaxOptions };
package/dist/index.js CHANGED
@@ -82,6 +82,7 @@ var products = pgTable(
82
82
  quantity: integer("quantity").notNull().default(1),
83
83
  status: productStatus("status").notNull().default("draft"),
84
84
  featured: boolean("featured").notNull().default(false),
85
+ taxCode: text("tax_code"),
85
86
  publishedAt: timestamp("published_at", { withTimezone: true }),
86
87
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
87
88
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
@@ -139,6 +140,7 @@ var orders = pgTable("commerce_orders", {
139
140
  discountCode: text("discount_code"),
140
141
  shippingCents: integer("shipping_cents").notNull().default(0),
141
142
  shippingLabel: text("shipping_label"),
143
+ taxCents: integer("tax_cents").notNull().default(0),
142
144
  currency: text("currency").notNull(),
143
145
  checkoutSessionId: text("checkout_session_id").unique(),
144
146
  shippingAddress: jsonb("shipping_address"),
@@ -548,6 +550,7 @@ function toAdminProduct({ row, images }) {
548
550
  status: row.status,
549
551
  featured: row.featured,
550
552
  everPublished: row.publishedAt !== null,
553
+ taxCode: row.taxCode,
551
554
  images: images.map((i) => ({
552
555
  id: i.id,
553
556
  url: i.url,
@@ -1226,7 +1229,8 @@ async function findPurchasable(db, ids) {
1226
1229
  id: row.id,
1227
1230
  name: row.name,
1228
1231
  priceCents: completeFields(row).priceCents,
1229
- currency: row.currency
1232
+ currency: row.currency,
1233
+ taxCode: row.taxCode
1230
1234
  }));
1231
1235
  }
1232
1236
 
@@ -1318,7 +1322,8 @@ async function selectOrderPage(db, filter) {
1318
1322
  id: orders.id,
1319
1323
  status: orders.status,
1320
1324
  email: orders.email,
1321
- totalCents: sql5`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents}`,
1325
+ taxCents: orders.taxCents,
1326
+ totalCents: sql5`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents} + ${orders.taxCents}`,
1322
1327
  currency: orders.currency,
1323
1328
  itemCount,
1324
1329
  createdAt: orders.createdAt,
@@ -1341,7 +1346,7 @@ var isRecordedRefund = or4(
1341
1346
  async function lockOrderForRefund(tx, id) {
1342
1347
  const [row] = await tx.select({
1343
1348
  status: orders.status,
1344
- totalCents: sql6`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents}`,
1349
+ totalCents: sql6`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents} + ${orders.taxCents}`,
1345
1350
  currency: orders.currency,
1346
1351
  checkoutSessionId: orders.checkoutSessionId
1347
1352
  }).from(orders).where(eq6(orders.id, id)).for("update");
@@ -1433,6 +1438,7 @@ async function saveOrderDetails(db, orderId, details, status) {
1433
1438
  shippingAddress: details.shippingAddress ?? null,
1434
1439
  shippingCents: details.shipping?.amountCents ?? 0,
1435
1440
  shippingLabel: details.shipping?.label ?? null,
1441
+ taxCents: details.taxCents ?? 0,
1436
1442
  updatedAt: sql7`now()`
1437
1443
  }).where(eq7(orders.id, orderId));
1438
1444
  }
@@ -1478,7 +1484,8 @@ async function toSummary(db, order) {
1478
1484
  discountCode: order.discountCode,
1479
1485
  shippingCents: order.shippingCents,
1480
1486
  shippingLabel: order.shippingLabel,
1481
- totalCents: order.subtotalCents - order.discountCents + order.shippingCents,
1487
+ taxCents: order.taxCents,
1488
+ totalCents: order.subtotalCents - order.discountCents + order.shippingCents + order.taxCents,
1482
1489
  currency: order.currency,
1483
1490
  items: items.map((i) => ({
1484
1491
  productId: i.productId,
@@ -1558,6 +1565,7 @@ async function listAdminOrders(db, query) {
1558
1565
  id: r.id,
1559
1566
  status: r.status,
1560
1567
  email: r.email,
1568
+ taxCents: r.taxCents,
1561
1569
  totalCents: r.totalCents,
1562
1570
  currency: r.currency,
1563
1571
  itemCount: r.itemCount,
@@ -2536,6 +2544,15 @@ function assertDevCheckoutAllowed() {
2536
2544
  throw new Error("devCheckout is not allowed when NODE_ENV=production");
2537
2545
  }
2538
2546
  }
2547
+ function completedWithTax(payment, session, buyer) {
2548
+ const details = resolveDevCompletion(
2549
+ buyer,
2550
+ session.id,
2551
+ session.shippingRates[0]
2552
+ );
2553
+ const taxCents = payment.taxFor(session.id, details.shipping?.amountCents);
2554
+ return payment.completed(session.id, { ...details, taxCents });
2555
+ }
2539
2556
  var devCheckoutRoutes = async (app, { db, payment, urls, hooks, buyer }) => {
2540
2557
  assertDevCheckoutAllowed();
2541
2558
  app.get("/checkout/dev/:sessionId/complete", async (req, reply) => {
@@ -2545,10 +2562,7 @@ var devCheckoutRoutes = async (app, { db, payment, urls, hooks, buyer }) => {
2545
2562
  return reply.code(404).send({ error: "SESSION_NOT_FOUND" });
2546
2563
  }
2547
2564
  const expire = req.query.outcome === "expired";
2548
- const { rawBody, signature } = expire ? payment.expired(sessionId) : payment.completed(
2549
- sessionId,
2550
- resolveDevCompletion(buyer, sessionId, session.shippingRates[0])
2551
- );
2565
+ const { rawBody, signature } = expire ? payment.expired(sessionId) : completedWithTax(payment, session, buyer);
2552
2566
  const event = await payment.verifyWebhook(rawBody, signature);
2553
2567
  await handlePaymentEvent(db, event, hooks, req.log);
2554
2568
  return reply.redirect(expire ? urls.cancel : urls.success(sessionId), 302);
@@ -2632,7 +2646,8 @@ async function priceLines(db, requests, currency) {
2632
2646
  productId,
2633
2647
  name: product.name,
2634
2648
  unitAmountCents: product.priceCents,
2635
- quantity
2649
+ quantity,
2650
+ ...product.taxCode && { taxCode: product.taxCode }
2636
2651
  };
2637
2652
  });
2638
2653
  }
@@ -2822,7 +2837,8 @@ var ordersPlugin = async (app, { db, ...settings }) => {
2822
2837
  // src/payment/fake-gateway.ts
2823
2838
  import { createHmac, randomUUID as randomUUID4, timingSafeEqual } from "crypto";
2824
2839
  function createFakeGateway({
2825
- secret = "fake-webhook-secret"
2840
+ secret = "fake-webhook-secret",
2841
+ tax
2826
2842
  } = {}) {
2827
2843
  const sessions = [];
2828
2844
  const refunds = [];
@@ -2925,7 +2941,17 @@ function createFakeGateway({
2925
2941
  refunds: refunds2
2926
2942
  });
2927
2943
  },
2928
- sign
2944
+ sign,
2945
+ taxFor(sessionId, shippingCents = 0) {
2946
+ const session = sessions.find((s) => s.id === sessionId);
2947
+ if (!tax || !session) return 0;
2948
+ const merchandise = session.lines.reduce(
2949
+ (sum, l) => sum + l.unitAmountCents * l.quantity,
2950
+ 0
2951
+ );
2952
+ const taxable = merchandise - (session.discount?.amountCents ?? 0) + shippingCents;
2953
+ return Math.round(taxable * tax.rate);
2954
+ }
2929
2955
  };
2930
2956
  }
2931
2957
  function isFakeGateway(gateway) {
@@ -3041,13 +3067,17 @@ function toShippingAddress(name, address) {
3041
3067
  Object.entries(fields).filter(([, v]) => v !== void 0)
3042
3068
  );
3043
3069
  }
3044
- function toShippingOption(rate, currency) {
3070
+ function toShippingOption(rate, currency, tax) {
3045
3071
  const days = (value) => ({ unit: "business_day", value });
3046
3072
  return {
3047
3073
  shipping_rate_data: {
3048
3074
  type: "fixed_amount",
3049
3075
  display_name: rate.label,
3050
3076
  fixed_amount: { amount: rate.amountCents, currency },
3077
+ ...tax && {
3078
+ tax_behavior: tax.behavior ?? "exclusive",
3079
+ ...tax.shippingTaxCode && { tax_code: tax.shippingTaxCode }
3080
+ },
3051
3081
  ...rate.minDays !== void 0 && rate.maxDays !== void 0 && {
3052
3082
  delivery_estimate: {
3053
3083
  minimum: days(rate.minDays),
@@ -3063,7 +3093,7 @@ function toChosenShipping(session) {
3063
3093
  if (!cost || !rate || typeof rate === "string") return void 0;
3064
3094
  return {
3065
3095
  label: rate.display_name ?? "Shipping",
3066
- amountCents: cost.amount_total
3096
+ amountCents: cost.amount_subtotal
3067
3097
  };
3068
3098
  }
3069
3099
  function toPaymentEvent(event, expanded) {
@@ -3077,6 +3107,9 @@ function toPaymentEvent(event, expanded) {
3077
3107
  email: session.customer_details?.email ?? void 0,
3078
3108
  shippingAddress: toShippingAddress(shipping?.name, shipping?.address),
3079
3109
  shipping: expanded && toChosenShipping(expanded),
3110
+ ...session.total_details && {
3111
+ taxCents: session.total_details.amount_tax
3112
+ },
3080
3113
  paymentPending: session.payment_status === "unpaid"
3081
3114
  };
3082
3115
  }
@@ -3145,7 +3178,8 @@ function createCoupon(stripe, { discount, currency, expiresAt }) {
3145
3178
  function createStripeGateway({
3146
3179
  secretKey,
3147
3180
  webhookSecret,
3148
- client
3181
+ client,
3182
+ tax
3149
3183
  }) {
3150
3184
  let stripe = client ? Promise.resolve(client) : void 0;
3151
3185
  const getStripe = () => stripe ??= loadStripe(secretKey);
@@ -3165,19 +3199,22 @@ function createStripeGateway({
3165
3199
  price_data: {
3166
3200
  currency: input.currency.toLowerCase(),
3167
3201
  unit_amount: line.unitAmountCents,
3202
+ ...tax && { tax_behavior: tax.behavior ?? "exclusive" },
3168
3203
  product_data: {
3169
3204
  name: line.name,
3170
- metadata: { productId: line.productId }
3205
+ metadata: { productId: line.productId },
3206
+ ...tax && line.taxCode && { tax_code: line.taxCode }
3171
3207
  }
3172
3208
  }
3173
3209
  })),
3210
+ ...tax && { automatic_tax: { enabled: true } },
3174
3211
  ...coupon && { discounts: [{ coupon: coupon.id }] },
3175
3212
  success_url: input.successUrl,
3176
3213
  cancel_url: input.cancelUrl,
3177
3214
  expires_at: Math.floor(input.expiresAt.getTime() / 1e3),
3178
3215
  ...input.shippingRates.length > 0 && {
3179
3216
  shipping_options: input.shippingRates.map(
3180
- (rate) => toShippingOption(rate, input.currency.toLowerCase())
3217
+ (rate) => toShippingOption(rate, input.currency.toLowerCase(), tax)
3181
3218
  )
3182
3219
  },
3183
3220
  shipping_address_collection: {
@@ -3257,6 +3294,7 @@ var defaultLabels = {
3257
3294
  subtotal: "Subtotal",
3258
3295
  discount: (code) => code ? `Discount (${code})` : "Discount",
3259
3296
  shipping: (label) => label ? `Shipping (${label})` : "Shipping",
3297
+ tax: "Tax",
3260
3298
  total: "Total"
3261
3299
  };
3262
3300
  var escapes = {
@@ -3312,15 +3350,23 @@ function orderEmailLines(order, options = {}) {
3312
3350
  label: labels.shipping(order.shippingLabel),
3313
3351
  amount: money(order.shippingCents)
3314
3352
  };
3353
+ const tax = order.taxCents > 0 ? { label: labels.tax, amount: money(order.taxCents) } : void 0;
3315
3354
  const total = { label: labels.total, amount: money(order.totalCents) };
3316
3355
  const address = addressLines(order);
3317
- const totals = [subtotal, ...discount ? [discount] : [], shipping, total];
3356
+ const totals = [
3357
+ subtotal,
3358
+ ...discount ? [discount] : [],
3359
+ shipping,
3360
+ ...tax ? [tax] : [],
3361
+ total
3362
+ ];
3318
3363
  return {
3319
3364
  reference: formatOrderRef(order.id),
3320
3365
  items,
3321
3366
  subtotal,
3322
3367
  discount,
3323
3368
  shipping,
3369
+ tax,
3324
3370
  total,
3325
3371
  address,
3326
3372
  text: {
@@ -3492,7 +3538,7 @@ async function openStoredFile(dir, key) {
3492
3538
  }
3493
3539
 
3494
3540
  // src/payment/resolve.ts
3495
- function resolvePayment(env = process.env) {
3541
+ function resolvePayment(env = process.env, { tax, fakeTax } = {}) {
3496
3542
  const secretKey = env.STRIPE_SECRET_KEY || void 0;
3497
3543
  const webhookSecret = env.STRIPE_WEBHOOK_SECRET || void 0;
3498
3544
  if (!secretKey !== !webhookSecret) {
@@ -3509,9 +3555,13 @@ function resolvePayment(env = process.env) {
3509
3555
  "The fake payment gateway is not allowed in production; set the Stripe keys"
3510
3556
  );
3511
3557
  }
3512
- return createFakeGateway();
3558
+ return createFakeGateway({ tax: fakeTax });
3513
3559
  }
3514
- return createStripeGateway({ secretKey, webhookSecret });
3560
+ return createStripeGateway({
3561
+ secretKey,
3562
+ webhookSecret,
3563
+ tax
3564
+ });
3515
3565
  }
3516
3566
  function checkoutUrls(webUrl, {
3517
3567
  successPath = "/checkout/success",