founder-schema 0.11.0 → 0.12.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.
Files changed (81) hide show
  1. package/README.md +65 -0
  2. package/dist/access/access.d.ts +10 -6
  3. package/dist/access/access.d.ts.map +1 -1
  4. package/dist/access/access.js +8 -1
  5. package/dist/access/access.js.map +1 -1
  6. package/dist/access/permissions.d.ts +60 -3
  7. package/dist/access/permissions.d.ts.map +1 -1
  8. package/dist/access/permissions.js +24 -0
  9. package/dist/access/permissions.js.map +1 -1
  10. package/dist/access/roles.d.ts +3 -3
  11. package/dist/access/standing.d.ts +59 -0
  12. package/dist/access/standing.d.ts.map +1 -0
  13. package/dist/access/standing.js +52 -0
  14. package/dist/access/standing.js.map +1 -0
  15. package/dist/core/errors.d.ts +47 -1
  16. package/dist/core/errors.d.ts.map +1 -1
  17. package/dist/core/errors.js +82 -1
  18. package/dist/core/errors.js.map +1 -1
  19. package/dist/core/limits.d.ts +29 -3
  20. package/dist/core/limits.d.ts.map +1 -1
  21. package/dist/core/limits.js +30 -3
  22. package/dist/core/limits.js.map +1 -1
  23. package/dist/core/responses.d.ts +55 -0
  24. package/dist/core/responses.d.ts.map +1 -1
  25. package/dist/core/responses.js +6 -0
  26. package/dist/core/responses.js.map +1 -1
  27. package/dist/core/validation.d.ts +14 -0
  28. package/dist/core/validation.d.ts.map +1 -1
  29. package/dist/core/validation.js +14 -0
  30. package/dist/core/validation.js.map +1 -1
  31. package/dist/domains/audit/audit.d.ts +28 -7
  32. package/dist/domains/audit/audit.d.ts.map +1 -1
  33. package/dist/domains/audit/audit.js +17 -0
  34. package/dist/domains/audit/audit.js.map +1 -1
  35. package/dist/domains/billing/billing.d.ts +1179 -0
  36. package/dist/domains/billing/billing.d.ts.map +1 -0
  37. package/dist/domains/billing/billing.js +393 -0
  38. package/dist/domains/billing/billing.js.map +1 -0
  39. package/dist/domains/billing/gst.d.ts +229 -0
  40. package/dist/domains/billing/gst.d.ts.map +1 -0
  41. package/dist/domains/billing/gst.js +117 -0
  42. package/dist/domains/billing/gst.js.map +1 -0
  43. package/dist/domains/chat/chat.d.ts +85 -0
  44. package/dist/domains/chat/chat.d.ts.map +1 -1
  45. package/dist/domains/chat/chat.js +62 -0
  46. package/dist/domains/chat/chat.js.map +1 -1
  47. package/dist/domains/communities/communities.d.ts +11 -11
  48. package/dist/domains/dashboard/dashboard.d.ts +9 -1
  49. package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
  50. package/dist/domains/dashboard/dashboard.js +15 -0
  51. package/dist/domains/dashboard/dashboard.js.map +1 -1
  52. package/dist/domains/feed/feed.d.ts.map +1 -1
  53. package/dist/domains/feed/feed.js +7 -1
  54. package/dist/domains/feed/feed.js.map +1 -1
  55. package/dist/domains/members/members.d.ts +30 -5
  56. package/dist/domains/members/members.d.ts.map +1 -1
  57. package/dist/domains/members/members.js +23 -0
  58. package/dist/domains/members/members.js.map +1 -1
  59. package/dist/domains/membership/membership.d.ts +239 -0
  60. package/dist/domains/membership/membership.d.ts.map +1 -0
  61. package/dist/domains/membership/membership.js +153 -0
  62. package/dist/domains/membership/membership.js.map +1 -0
  63. package/dist/domains/notifications/notifications.d.ts +43 -1
  64. package/dist/domains/notifications/notifications.d.ts.map +1 -1
  65. package/dist/domains/notifications/notifications.js +19 -0
  66. package/dist/domains/notifications/notifications.js.map +1 -1
  67. package/dist/domains/products/products.d.ts +44 -12
  68. package/dist/domains/products/products.d.ts.map +1 -1
  69. package/dist/domains/products/products.js +33 -3
  70. package/dist/domains/products/products.js.map +1 -1
  71. package/dist/domains/profile/founder-profile.d.ts +1 -1
  72. package/dist/domains/reviews/reviews.d.ts +3 -0
  73. package/dist/domains/reviews/reviews.d.ts.map +1 -1
  74. package/dist/domains/reviews/reviews.js +6 -1
  75. package/dist/domains/reviews/reviews.js.map +1 -1
  76. package/dist/domains/team/team.d.ts +4 -4
  77. package/dist/index.d.ts +6 -3
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +8 -3
  80. package/dist/index.js.map +1 -1
  81. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../../src/domains/billing/billing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,OAAO,EACL,KAAK,cAAc,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,UAAU,EACV,KAAK,YAAY,EACjB,kBAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,YAAY,GACb,MAAM,UAAU,CAAC;AAIlB,4CAA4C;AAC5C,eAAO,MAAM,oBAAoB,4CAA6C,CAAC;AAC/E,eAAO,MAAM,kBAAkB;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;2BAI7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,wDAMO,CAAC;AAIrC;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;iBAOzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,8EAA8E;AAC9E,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAI1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,kEAAkE;AAClE,eAAO,MAAM,gBAAgB;;;;;;;;iBAO3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBActB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAIhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,wGAQtB,CAAC;AACX,eAAO,MAAM,iBAAiB;;;;;;;;EAA8B,CAAC;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;6BAsBpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,iBAAiB;;iBAAwC,CAAC;AACvE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,qEAAqE;AACrE,eAAO,MAAM,sBAAsB;;;;iBAIjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,sCAAsC;AACtC,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAU3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI1D,sGAAsG;AACtG,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,2DAA2D;AAC3D,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBxB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,mDAAmD;AACnD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAM3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,uGAAuG;AACvG,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAW/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAwC,CAAC;AACvE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAIhE,iGAAiG;AACjG,eAAO,MAAM,WAAW;;;;iBAItB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;iBAKnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE/E,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA6C,CAAC;AACjF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAI1E,iFAAiF;AACjF,eAAO,MAAM,uBAAuB;;iBAAqC,CAAC;AAC1E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;iBAGlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,gDAAiD,CAAC;AACnF,eAAO,MAAM,kBAAkB;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;2BAa9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;iBAavB,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD,kDAAkD;AAClD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;iBAA6C,CAAC;AAC3E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,kBAAkB;;iBAAyC,CAAC;AACzE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAK9D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;iBAS4D,CAAC;AAChG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;iBAQ4D,CAAC;AAChG,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -0,0 +1,393 @@
1
+ /**
2
+ * founder-schema: buying, paying, invoices, refunds and coupons (0.12.0)
3
+ *
4
+ * POST /api/v1/billing/quote quoteBodySchema -> quoteSchema
5
+ * POST /api/v1/billing/orders createOrderBodySchema -> createOrderResponseSchema
6
+ * GET /api/v1/billing/orders/:orderId orderStateSchema (polled while "processing")
7
+ * POST /api/v1/billing/orders/:orderId/confirm confirmOrderBodySchema -> orderStateSchema
8
+ * GET /api/v1/billing/payments paymentPageSchema (mine)
9
+ * GET /api/v1/billing/payments/:orderId paymentDetailSchema (mine, with the invoice)
10
+ *
11
+ * Staff (by the ability: support reads; managers refund and run coupons):
12
+ * GET /api/v1/staff/payments staffPaymentsQuerySchema -> staffPaymentPageSchema
13
+ * GET /api/v1/staff/payments/:orderId staffPaymentDetailSchema
14
+ * POST /api/v1/staff/payments/:orderId/refund refundPaymentBodySchema -> staffPaymentDetailSchema
15
+ * GET /api/v1/staff/coupons couponListSchema
16
+ * POST /api/v1/staff/coupons createCouponBodySchema -> couponSchema
17
+ * PATCH /api/v1/staff/coupons/:couponId updateCouponBodySchema -> couponSchema
18
+ * PUT /api/v1/members/:memberId/membership setMembershipBodySchema -> memberDetailSchema
19
+ *
20
+ * THE ONE RULE: THE SERVER PRICES, THE CLIENT CHOOSES. A request names what it
21
+ * wants (an item) and maybe a coupon CODE; it never names an amount. Every
22
+ * amount here is the server's, in integer paise, GST included.
23
+ *
24
+ * TWO PATHS IN, ONE GRANT. A payment reaches the server from the browser the
25
+ * moment checkout returns (`confirm`) and from the gateway's webhook. Either
26
+ * grants; the second is a no-op. So a member who closes the tab is still
27
+ * granted, and `confirm` failing never means "not paid": the web says
28
+ * "processing" and polls the order.
29
+ *
30
+ * GST: the company is registered, every captured payment gets a tax invoice,
31
+ * every refund a credit note. Where the buyer is decides the tax: in the
32
+ * supplier's state CGST + SGST, elsewhere IGST. So checkout asks for the state.
33
+ *
34
+ * @module founder-schema/domains/billing
35
+ */
36
+ import { z } from 'zod';
37
+ import { emptyStringToUndefined, objectIdSchema, paginationQuerySchema, } from '../../core/common.js';
38
+ import { BILLING_REASON_MAX, BILLING_REASON_MIN, COUPON_CODE_MAX, COUPON_CODE_MIN, COUPON_LABEL_MAX, } from '../../core/limits.js';
39
+ import { paginatedSchema } from '../../core/responses.js';
40
+ import { VALIDATION_MESSAGE } from '../../core/validation.js';
41
+ import { paiseSchema, planPeriodSchema, planTierSchema } from '../membership/membership.js';
42
+ import { billingDetailsSchema, gstStateCodeSchema } from './gst.js';
43
+ export { billingDetailsSchema, GST_RATE_PERCENT, GST_STATE_CODE_VALUES, GST_STATES, gstinChecksumValid, gstinSchema, gstStateCodeSchema, gstStateName, } from './gst.js';
44
+ // ─── What can be bought ──────────────────────────────────────────────────────
45
+ /** ⚠ Stored on every order: append only. */
46
+ export const PURCHASE_KIND_VALUES = ['membership', 'plan', 'upgrade'];
47
+ export const purchaseKindSchema = z.enum(PURCHASE_KIND_VALUES);
48
+ /**
49
+ * The thing asked for.
50
+ *
51
+ * membership Rs 99, once
52
+ * plan a month or a year of Premium or Business, appended
53
+ * upgrade the Premium time left, turned into Business, for the difference
54
+ */
55
+ export const purchaseItemSchema = z.discriminatedUnion('kind', [
56
+ z.object({ kind: z.literal('membership') }),
57
+ z.object({ kind: z.literal('plan'), tier: planTierSchema, period: planPeriodSchema }),
58
+ z.object({ kind: z.literal('upgrade') }),
59
+ ]);
60
+ /** A coupon code as typed: letters, digits, hyphens; compared upper-case. */
61
+ export const couponCodeSchema = z
62
+ .string()
63
+ .trim()
64
+ .min(COUPON_CODE_MIN)
65
+ .max(COUPON_CODE_MAX)
66
+ .regex(/^[A-Za-z0-9-]+$/, VALIDATION_MESSAGE.couponCodeInvalid)
67
+ .transform((v) => v.toUpperCase());
68
+ // ─── Quote ───────────────────────────────────────────────────────────────────
69
+ /**
70
+ * The tax inside a total. `taxable + cgst + sgst + igst = total`; either
71
+ * CGST + SGST (buyer in the supplier's state) or IGST, never both.
72
+ */
73
+ export const taxSplitSchema = z.object({
74
+ ratePercent: z.number().int().positive(),
75
+ taxable: paiseSchema,
76
+ cgst: paiseSchema,
77
+ sgst: paiseSchema,
78
+ igst: paiseSchema,
79
+ total: paiseSchema,
80
+ });
81
+ /** `POST /billing/quote`. The state is optional: without it, no tax split. */
82
+ export const quoteBodySchema = z.object({
83
+ item: purchaseItemSchema,
84
+ couponCode: couponCodeSchema.optional(),
85
+ stateCode: gstStateCodeSchema.optional(),
86
+ });
87
+ /** What a plan or an upgrade would do to the plan, priced now. */
88
+ export const planEffectSchema = z.object({
89
+ tier: planTierSchema,
90
+ /** When this purchase's time starts (later than now when it waits its turn). */
91
+ startsAt: z.iso.datetime(),
92
+ endsAt: z.iso.datetime(),
93
+ /** For an upgrade: the days of Premium turned into Business. */
94
+ upgradedDays: z.number().int().positive().nullable(),
95
+ });
96
+ export const quoteSchema = z.object({
97
+ item: purchaseItemSchema,
98
+ /** "Membership", "Premium, 1 month", "Upgrade to Business": the server's words. */
99
+ title: z.string(),
100
+ listPrice: paiseSchema,
101
+ discount: paiseSchema,
102
+ /** What would be charged. Zero for a free-membership coupon: no checkout at all. */
103
+ total: paiseSchema,
104
+ coupon: z.object({ code: z.string(), label: z.string() }).nullable(),
105
+ /** Null for a membership. */
106
+ plan: planEffectSchema.nullable(),
107
+ /** Null without a state. */
108
+ tax: taxSplitSchema.nullable(),
109
+ refundWindowDays: z.number().int().positive(),
110
+ });
111
+ // ─── Orders ──────────────────────────────────────────────────────────────────
112
+ /**
113
+ * Where an order stands. ⚠ Stored: append only.
114
+ *
115
+ * created checkout open, nothing paid yet
116
+ * paid captured and granted; has an invoice
117
+ * granted_free a free coupon: granted with no payment and no invoice
118
+ * expired not paid within `ORDER_TTL_MINUTES`
119
+ * refund_pending staff refunded it; the gateway is returning the money
120
+ * refunded the money is back; has a credit note
121
+ * refund_failed the gateway could not return it; staff act from its dashboard
122
+ */
123
+ export const ORDER_STATUS_VALUES = [
124
+ 'created',
125
+ 'paid',
126
+ 'granted_free',
127
+ 'expired',
128
+ 'refund_pending',
129
+ 'refunded',
130
+ 'refund_failed',
131
+ ];
132
+ export const orderStatusSchema = z.enum(ORDER_STATUS_VALUES);
133
+ /**
134
+ * `POST /billing/orders`. Billing details are required for anything paid (the
135
+ * invoice needs a place of supply): the server refuses a paid order without
136
+ * them (`billing.stateCode`, VALIDATION_ERROR). A free coupon issues no
137
+ * invoice, so it needs none (0.12.0).
138
+ */
139
+ export const createOrderBodySchema = z.object({
140
+ item: purchaseItemSchema,
141
+ couponCode: couponCodeSchema.optional(),
142
+ billing: billingDetailsSchema.optional(),
143
+ });
144
+ /**
145
+ * Either done (a free coupon granted at once) or a checkout to open. The
146
+ * checkout fields are Razorpay Checkout's own options; identity (name, email,
147
+ * phone) is the server's, never typed by the caller.
148
+ */
149
+ export const createOrderResponseSchema = z.discriminatedUnion('status', [
150
+ z.object({ status: z.literal('granted'), orderId: objectIdSchema }),
151
+ z.object({
152
+ status: z.literal('checkout'),
153
+ orderId: objectIdSchema,
154
+ expiresAt: z.iso.datetime(),
155
+ checkout: z.object({
156
+ keyId: z.string().min(1),
157
+ razorpayOrderId: z.string().min(1),
158
+ amount: paiseSchema,
159
+ currency: z.literal('INR'),
160
+ /** The merchant name on the dialog. */
161
+ name: z.string(),
162
+ /** What is being bought, under the name. */
163
+ description: z.string(),
164
+ prefill: z.object({
165
+ name: z.string(),
166
+ email: z.string(),
167
+ contact: z.string().optional(),
168
+ }),
169
+ }),
170
+ }),
171
+ ]);
172
+ export const orderParamsSchema = z.object({ orderId: objectIdSchema });
173
+ /** What Razorpay Checkout's `handler` hands back, as it names it. */
174
+ export const confirmOrderBodySchema = z.object({
175
+ razorpayOrderId: z.string().min(1).max(64),
176
+ razorpayPaymentId: z.string().min(1).max(64),
177
+ razorpaySignature: z.string().min(1).max(256),
178
+ });
179
+ /** An order as its buyer polls it. */
180
+ export const orderStateSchema = z.object({
181
+ orderId: objectIdSchema,
182
+ item: purchaseItemSchema,
183
+ status: orderStatusSchema,
184
+ /**
185
+ * The money was taken but what it bought could not be applied (bought
186
+ * twice in two tabs, the plan changed while paying): it is refunded in
187
+ * full, by itself. The client says so instead of "done".
188
+ */
189
+ notApplied: z.boolean(),
190
+ });
191
+ // ─── Payments, invoices, credit notes ────────────────────────────────────────
192
+ /** A party on an invoice. The supplier always has a GSTIN on a real invoice; a buyer, only on B2B. */
193
+ export const invoicePartySchema = z.object({
194
+ name: z.string(),
195
+ address: z.string().nullable(),
196
+ gstin: z.string().nullable(),
197
+ stateCode: gstStateCodeSchema,
198
+ stateName: z.string(),
199
+ });
200
+ /** A tax invoice, frozen when the payment was captured. */
201
+ export const invoiceSchema = z.object({
202
+ /** e.g. `FC/26-27/000123`: sequential within a financial year, at most 16 characters. */
203
+ number: z.string().max(16),
204
+ issuedAt: z.iso.datetime(),
205
+ /**
206
+ * Issued where the supplier's GSTIN is not configured (dev, Razorpay test
207
+ * mode): marked "TEST: not a tax invoice". Never true in production.
208
+ */
209
+ test: z.boolean(),
210
+ supplier: invoicePartySchema,
211
+ buyer: invoicePartySchema,
212
+ /** B2B with the buyer's GSTIN, B2C without. */
213
+ kind: z.enum(['b2b', 'b2c']),
214
+ /** The service's accounting code (SAC). */
215
+ sac: z.string(),
216
+ description: z.string(),
217
+ tax: taxSplitSchema,
218
+ /** Always false: these services are never under reverse charge. */
219
+ reverseCharge: z.literal(false),
220
+ razorpayPaymentId: z.string(),
221
+ });
222
+ /** A refund's credit note, against its invoice. */
223
+ export const creditNoteSchema = z.object({
224
+ /** e.g. `FCCN/26-27/00012`: the invoice series + `CN`, sequential within a financial year. */
225
+ number: z.string().max(16),
226
+ issuedAt: z.iso.datetime(),
227
+ invoiceNumber: z.string(),
228
+ tax: taxSplitSchema,
229
+ });
230
+ /** One purchase in a member's history. Only settled orders are listed, never an abandoned checkout. */
231
+ export const paymentSummarySchema = z.object({
232
+ id: objectIdSchema,
233
+ item: purchaseItemSchema,
234
+ title: z.string(),
235
+ total: paiseSchema,
236
+ status: orderStatusSchema,
237
+ /** When it was captured, or granted for a free coupon. */
238
+ paidAt: z.iso.datetime(),
239
+ invoiceNumber: z.string().nullable(),
240
+ /** Until when a refund may be asked for; null once refunded or for a free coupon. */
241
+ refundableUntil: z.iso.datetime().nullable(),
242
+ });
243
+ export const paymentPageSchema = paginatedSchema(paymentSummarySchema);
244
+ export const paymentDetailSchema = paymentSummarySchema.extend({
245
+ listPrice: paiseSchema,
246
+ discount: paiseSchema,
247
+ coupon: z.object({ code: z.string(), label: z.string() }).nullable(),
248
+ invoice: invoiceSchema.nullable(),
249
+ creditNote: creditNoteSchema.nullable(),
250
+ refundedAt: z.iso.datetime().nullable(),
251
+ });
252
+ // ─── Staff: payments and refunds ─────────────────────────────────────────────
253
+ /** Who paid, as they are now, so staff can find them. Email only for a role that may read it. */
254
+ export const payerSchema = z.object({
255
+ id: objectIdSchema,
256
+ displayName: z.string(),
257
+ email: z.string().nullable(),
258
+ });
259
+ export const staffPaymentsQuerySchema = paginationQuerySchema.extend({
260
+ status: orderStatusSchema.optional(),
261
+ memberId: objectIdSchema.optional(),
262
+ /** Only the ones a person must act on (`needsAction` below). */
263
+ needsAction: z.preprocess(emptyStringToUndefined, z.stringbool().default(false)),
264
+ });
265
+ export const staffPaymentSummarySchema = paymentSummarySchema.extend({
266
+ payer: payerSchema,
267
+ /**
268
+ * A person must act (0.12.0): taken but not applied and not yet refunded
269
+ * (the automatic refund did not go through), a refund that failed at the
270
+ * gateway, or an open chargeback.
271
+ */
272
+ needsAction: z.boolean(),
273
+ });
274
+ export const staffPaymentPageSchema = paginatedSchema(staffPaymentSummarySchema);
275
+ export const staffPaymentDetailSchema = paymentDetailSchema.extend({
276
+ payer: payerSchema,
277
+ razorpay: z.object({
278
+ orderId: z.string().nullable(),
279
+ paymentId: z.string().nullable(),
280
+ refundId: z.string().nullable(),
281
+ }),
282
+ /** Why staff refunded it, on record. */
283
+ refundReason: z.string().nullable(),
284
+ /**
285
+ * The money was taken but what it bought could not be applied when it was
286
+ * captured (they were a member already, the Premium time to upgrade had
287
+ * run out, past the 3-year cap): refund it. Null when it applied.
288
+ */
289
+ grantFailed: z.string().nullable(),
290
+ /** A chargeback was opened on it (cleared when it is won or closed). */
291
+ disputedAt: z.iso.datetime().nullable(),
292
+ /**
293
+ * What refunds made outside Founder Circle (the Razorpay dashboard) have
294
+ * already given back of this payment. A Refund here gives back the rest:
295
+ * `total` less this. 0 when there were none.
296
+ */
297
+ refundedOutside: paiseSchema,
298
+ /**
299
+ * Payments captured on this order other than the one that settled it (a
300
+ * late authorisation, a second Checkout attempt): each is refunded by
301
+ * itself; `refunded: false` means that refund has not gone through yet and
302
+ * the payment needs a person.
303
+ */
304
+ strayPayments: z.array(z.object({
305
+ paymentId: z.string(),
306
+ amount: paiseSchema,
307
+ refunded: z.boolean(),
308
+ at: z.iso.datetime(),
309
+ })),
310
+ viewer: z.object({
311
+ /** Paid, within the window, not their own, and the asker may refund. */
312
+ canRefund: z.boolean(),
313
+ }),
314
+ });
315
+ const reasonSchema = z.string().trim().min(BILLING_REASON_MIN).max(BILLING_REASON_MAX);
316
+ /** `POST /staff/payments/:orderId/refund`. The reason is on the audit record. */
317
+ export const refundPaymentBodySchema = z.object({ reason: reasonSchema });
318
+ /**
319
+ * `PUT /members/:memberId/membership`: grant (a partner, a press account) or
320
+ * revoke (a fake or duplicate account holding a founding place). A paid one
321
+ * is never revoked: refund its payment.
322
+ */
323
+ export const setMembershipBodySchema = z.object({
324
+ granted: z.boolean(),
325
+ reason: reasonSchema,
326
+ });
327
+ // ─── Staff: coupons ──────────────────────────────────────────────────────────
328
+ /** What a coupon is good for. A percent coupon names what it applies to. */
329
+ export const COUPON_TARGET_VALUES = ['membership', 'premium', 'business'];
330
+ export const couponTargetSchema = z.enum(COUPON_TARGET_VALUES);
331
+ /**
332
+ * ⚠ Stored: append only.
333
+ *
334
+ * free_membership the membership at Rs 0; `founding` numbers its holders
335
+ * ("Founding member #N")
336
+ * percent_off a share off the listed price of what it applies to
337
+ */
338
+ export const couponBenefitSchema = z.discriminatedUnion('kind', [
339
+ z.object({ kind: z.literal('free_membership'), founding: z.boolean() }),
340
+ z.object({
341
+ kind: z.literal('percent_off'),
342
+ percent: z.number().int().min(1).max(90),
343
+ appliesTo: z
344
+ .array(couponTargetSchema)
345
+ .min(1)
346
+ .max(COUPON_TARGET_VALUES.length)
347
+ .refine((list) => new Set(list).size === list.length, {
348
+ error: VALIDATION_MESSAGE.couponTargetsDuplicate,
349
+ }),
350
+ }),
351
+ ]);
352
+ export const couponSchema = z.object({
353
+ id: objectIdSchema,
354
+ code: z.string(),
355
+ label: z.string(),
356
+ benefit: couponBenefitSchema,
357
+ /** Redemptions allowed in all; null is no cap. */
358
+ max: z.number().int().positive().nullable(),
359
+ /** Redemptions in force (a revoked founding place frees one). */
360
+ used: z.number().int().nonnegative(),
361
+ active: z.boolean(),
362
+ validFrom: z.iso.datetime().nullable(),
363
+ validUntil: z.iso.datetime().nullable(),
364
+ createdAt: z.iso.datetime(),
365
+ });
366
+ /** Never paged: coupons are few. Newest first. */
367
+ export const couponListSchema = z.object({ items: z.array(couponSchema) });
368
+ export const couponParamsSchema = z.object({ couponId: objectIdSchema });
369
+ const windowValid = (body) => !body.validFrom || !body.validUntil || Date.parse(body.validFrom) < Date.parse(body.validUntil);
370
+ export const createCouponBodySchema = z
371
+ .object({
372
+ code: couponCodeSchema,
373
+ label: z.string().trim().min(2).max(COUPON_LABEL_MAX),
374
+ benefit: couponBenefitSchema,
375
+ max: z.number().int().positive().nullable(),
376
+ validFrom: z.iso.datetime().nullable().optional(),
377
+ validUntil: z.iso.datetime().nullable().optional(),
378
+ })
379
+ .refine(windowValid, { path: ['validUntil'], error: VALIDATION_MESSAGE.couponWindowInvalid });
380
+ /**
381
+ * What may change on a live coupon. The code and the benefit never change: a
382
+ * redemption is a record of what a code gave when it was used.
383
+ */
384
+ export const updateCouponBodySchema = z
385
+ .object({
386
+ label: z.string().trim().min(2).max(COUPON_LABEL_MAX).optional(),
387
+ active: z.boolean().optional(),
388
+ max: z.number().int().positive().nullable().optional(),
389
+ validFrom: z.iso.datetime().nullable().optional(),
390
+ validUntil: z.iso.datetime().nullable().optional(),
391
+ })
392
+ .refine(windowValid, { path: ['validUntil'], error: VALIDATION_MESSAGE.couponWindowInvalid });
393
+ //# sourceMappingURL=billing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../../src/domains/billing/billing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema: buying, paying, invoices, refunds and coupons (0.12.0)\n *\n * POST /api/v1/billing/quote quoteBodySchema -> quoteSchema\n * POST /api/v1/billing/orders createOrderBodySchema -> createOrderResponseSchema\n * GET /api/v1/billing/orders/:orderId orderStateSchema (polled while \"processing\")\n * POST /api/v1/billing/orders/:orderId/confirm confirmOrderBodySchema -> orderStateSchema\n * GET /api/v1/billing/payments paymentPageSchema (mine)\n * GET /api/v1/billing/payments/:orderId paymentDetailSchema (mine, with the invoice)\n *\n * Staff (by the ability: support reads; managers refund and run coupons):\n * GET /api/v1/staff/payments staffPaymentsQuerySchema -> staffPaymentPageSchema\n * GET /api/v1/staff/payments/:orderId staffPaymentDetailSchema\n * POST /api/v1/staff/payments/:orderId/refund refundPaymentBodySchema -> staffPaymentDetailSchema\n * GET /api/v1/staff/coupons couponListSchema\n * POST /api/v1/staff/coupons createCouponBodySchema -> couponSchema\n * PATCH /api/v1/staff/coupons/:couponId updateCouponBodySchema -> couponSchema\n * PUT /api/v1/members/:memberId/membership setMembershipBodySchema -> memberDetailSchema\n *\n * THE ONE RULE: THE SERVER PRICES, THE CLIENT CHOOSES. A request names what it\n * wants (an item) and maybe a coupon CODE; it never names an amount. Every\n * amount here is the server's, in integer paise, GST included.\n *\n * TWO PATHS IN, ONE GRANT. A payment reaches the server from the browser the\n * moment checkout returns (`confirm`) and from the gateway's webhook. Either\n * grants; the second is a no-op. So a member who closes the tab is still\n * granted, and `confirm` failing never means \"not paid\": the web says\n * \"processing\" and polls the order.\n *\n * GST: the company is registered, every captured payment gets a tax invoice,\n * every refund a credit note. Where the buyer is decides the tax: in the\n * supplier's state CGST + SGST, elsewhere IGST. So checkout asks for the state.\n *\n * @module founder-schema/domains/billing\n */\n\nimport { z } from 'zod';\nimport {\n emptyStringToUndefined,\n objectIdSchema,\n paginationQuerySchema,\n} from '../../core/common.js';\nimport {\n BILLING_REASON_MAX,\n BILLING_REASON_MIN,\n COUPON_CODE_MAX,\n COUPON_CODE_MIN,\n COUPON_LABEL_MAX,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\nimport { paiseSchema, planPeriodSchema, planTierSchema } from '../membership/membership.js';\nimport { billingDetailsSchema, gstStateCodeSchema } from './gst.js';\n\nexport {\n type BillingDetails,\n billingDetailsSchema,\n GST_RATE_PERCENT,\n GST_STATE_CODE_VALUES,\n GST_STATES,\n type GstStateCode,\n gstinChecksumValid,\n gstinSchema,\n gstStateCodeSchema,\n gstStateName,\n} from './gst.js';\n\n// ─── What can be bought ──────────────────────────────────────────────────────\n\n/** ⚠ Stored on every order: append only. */\nexport const PURCHASE_KIND_VALUES = ['membership', 'plan', 'upgrade'] as const;\nexport const purchaseKindSchema = z.enum(PURCHASE_KIND_VALUES);\nexport type PurchaseKind = z.infer<typeof purchaseKindSchema>;\n\n/**\n * The thing asked for.\n *\n * membership Rs 99, once\n * plan a month or a year of Premium or Business, appended\n * upgrade the Premium time left, turned into Business, for the difference\n */\nexport const purchaseItemSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('membership') }),\n z.object({ kind: z.literal('plan'), tier: planTierSchema, period: planPeriodSchema }),\n z.object({ kind: z.literal('upgrade') }),\n]);\nexport type PurchaseItem = z.infer<typeof purchaseItemSchema>;\n\n/** A coupon code as typed: letters, digits, hyphens; compared upper-case. */\nexport const couponCodeSchema = z\n .string()\n .trim()\n .min(COUPON_CODE_MIN)\n .max(COUPON_CODE_MAX)\n .regex(/^[A-Za-z0-9-]+$/, VALIDATION_MESSAGE.couponCodeInvalid)\n .transform((v) => v.toUpperCase());\n\n// ─── Quote ───────────────────────────────────────────────────────────────────\n\n/**\n * The tax inside a total. `taxable + cgst + sgst + igst = total`; either\n * CGST + SGST (buyer in the supplier's state) or IGST, never both.\n */\nexport const taxSplitSchema = z.object({\n ratePercent: z.number().int().positive(),\n taxable: paiseSchema,\n cgst: paiseSchema,\n sgst: paiseSchema,\n igst: paiseSchema,\n total: paiseSchema,\n});\nexport type TaxSplit = z.infer<typeof taxSplitSchema>;\n\n/** `POST /billing/quote`. The state is optional: without it, no tax split. */\nexport const quoteBodySchema = z.object({\n item: purchaseItemSchema,\n couponCode: couponCodeSchema.optional(),\n stateCode: gstStateCodeSchema.optional(),\n});\nexport type QuoteBody = z.infer<typeof quoteBodySchema>;\n\n/** What a plan or an upgrade would do to the plan, priced now. */\nexport const planEffectSchema = z.object({\n tier: planTierSchema,\n /** When this purchase's time starts (later than now when it waits its turn). */\n startsAt: z.iso.datetime(),\n endsAt: z.iso.datetime(),\n /** For an upgrade: the days of Premium turned into Business. */\n upgradedDays: z.number().int().positive().nullable(),\n});\nexport type PlanEffect = z.infer<typeof planEffectSchema>;\n\nexport const quoteSchema = z.object({\n item: purchaseItemSchema,\n /** \"Membership\", \"Premium, 1 month\", \"Upgrade to Business\": the server's words. */\n title: z.string(),\n listPrice: paiseSchema,\n discount: paiseSchema,\n /** What would be charged. Zero for a free-membership coupon: no checkout at all. */\n total: paiseSchema,\n coupon: z.object({ code: z.string(), label: z.string() }).nullable(),\n /** Null for a membership. */\n plan: planEffectSchema.nullable(),\n /** Null without a state. */\n tax: taxSplitSchema.nullable(),\n refundWindowDays: z.number().int().positive(),\n});\nexport type Quote = z.infer<typeof quoteSchema>;\n\n// ─── Orders ──────────────────────────────────────────────────────────────────\n\n/**\n * Where an order stands. ⚠ Stored: append only.\n *\n * created checkout open, nothing paid yet\n * paid captured and granted; has an invoice\n * granted_free a free coupon: granted with no payment and no invoice\n * expired not paid within `ORDER_TTL_MINUTES`\n * refund_pending staff refunded it; the gateway is returning the money\n * refunded the money is back; has a credit note\n * refund_failed the gateway could not return it; staff act from its dashboard\n */\nexport const ORDER_STATUS_VALUES = [\n 'created',\n 'paid',\n 'granted_free',\n 'expired',\n 'refund_pending',\n 'refunded',\n 'refund_failed',\n] as const;\nexport const orderStatusSchema = z.enum(ORDER_STATUS_VALUES);\nexport type OrderStatus = z.infer<typeof orderStatusSchema>;\n\n/**\n * `POST /billing/orders`. Billing details are required for anything paid (the\n * invoice needs a place of supply): the server refuses a paid order without\n * them (`billing.stateCode`, VALIDATION_ERROR). A free coupon issues no\n * invoice, so it needs none (0.12.0).\n */\nexport const createOrderBodySchema = z.object({\n item: purchaseItemSchema,\n couponCode: couponCodeSchema.optional(),\n billing: billingDetailsSchema.optional(),\n});\nexport type CreateOrderBody = z.infer<typeof createOrderBodySchema>;\n\n/**\n * Either done (a free coupon granted at once) or a checkout to open. The\n * checkout fields are Razorpay Checkout's own options; identity (name, email,\n * phone) is the server's, never typed by the caller.\n */\nexport const createOrderResponseSchema = z.discriminatedUnion('status', [\n z.object({ status: z.literal('granted'), orderId: objectIdSchema }),\n z.object({\n status: z.literal('checkout'),\n orderId: objectIdSchema,\n expiresAt: z.iso.datetime(),\n checkout: z.object({\n keyId: z.string().min(1),\n razorpayOrderId: z.string().min(1),\n amount: paiseSchema,\n currency: z.literal('INR'),\n /** The merchant name on the dialog. */\n name: z.string(),\n /** What is being bought, under the name. */\n description: z.string(),\n prefill: z.object({\n name: z.string(),\n email: z.string(),\n contact: z.string().optional(),\n }),\n }),\n }),\n]);\nexport type CreateOrderResponse = z.infer<typeof createOrderResponseSchema>;\n\nexport const orderParamsSchema = z.object({ orderId: objectIdSchema });\nexport type OrderParams = z.infer<typeof orderParamsSchema>;\n\n/** What Razorpay Checkout's `handler` hands back, as it names it. */\nexport const confirmOrderBodySchema = z.object({\n razorpayOrderId: z.string().min(1).max(64),\n razorpayPaymentId: z.string().min(1).max(64),\n razorpaySignature: z.string().min(1).max(256),\n});\nexport type ConfirmOrderBody = z.infer<typeof confirmOrderBodySchema>;\n\n/** An order as its buyer polls it. */\nexport const orderStateSchema = z.object({\n orderId: objectIdSchema,\n item: purchaseItemSchema,\n status: orderStatusSchema,\n /**\n * The money was taken but what it bought could not be applied (bought\n * twice in two tabs, the plan changed while paying): it is refunded in\n * full, by itself. The client says so instead of \"done\".\n */\n notApplied: z.boolean(),\n});\nexport type OrderState = z.infer<typeof orderStateSchema>;\n\n// ─── Payments, invoices, credit notes ────────────────────────────────────────\n\n/** A party on an invoice. The supplier always has a GSTIN on a real invoice; a buyer, only on B2B. */\nexport const invoicePartySchema = z.object({\n name: z.string(),\n address: z.string().nullable(),\n gstin: z.string().nullable(),\n stateCode: gstStateCodeSchema,\n stateName: z.string(),\n});\nexport type InvoiceParty = z.infer<typeof invoicePartySchema>;\n\n/** A tax invoice, frozen when the payment was captured. */\nexport const invoiceSchema = z.object({\n /** e.g. `FC/26-27/000123`: sequential within a financial year, at most 16 characters. */\n number: z.string().max(16),\n issuedAt: z.iso.datetime(),\n /**\n * Issued where the supplier's GSTIN is not configured (dev, Razorpay test\n * mode): marked \"TEST: not a tax invoice\". Never true in production.\n */\n test: z.boolean(),\n supplier: invoicePartySchema,\n buyer: invoicePartySchema,\n /** B2B with the buyer's GSTIN, B2C without. */\n kind: z.enum(['b2b', 'b2c']),\n /** The service's accounting code (SAC). */\n sac: z.string(),\n description: z.string(),\n tax: taxSplitSchema,\n /** Always false: these services are never under reverse charge. */\n reverseCharge: z.literal(false),\n razorpayPaymentId: z.string(),\n});\nexport type Invoice = z.infer<typeof invoiceSchema>;\n\n/** A refund's credit note, against its invoice. */\nexport const creditNoteSchema = z.object({\n /** e.g. `FCCN/26-27/00012`: the invoice series + `CN`, sequential within a financial year. */\n number: z.string().max(16),\n issuedAt: z.iso.datetime(),\n invoiceNumber: z.string(),\n tax: taxSplitSchema,\n});\nexport type CreditNote = z.infer<typeof creditNoteSchema>;\n\n/** One purchase in a member's history. Only settled orders are listed, never an abandoned checkout. */\nexport const paymentSummarySchema = z.object({\n id: objectIdSchema,\n item: purchaseItemSchema,\n title: z.string(),\n total: paiseSchema,\n status: orderStatusSchema,\n /** When it was captured, or granted for a free coupon. */\n paidAt: z.iso.datetime(),\n invoiceNumber: z.string().nullable(),\n /** Until when a refund may be asked for; null once refunded or for a free coupon. */\n refundableUntil: z.iso.datetime().nullable(),\n});\nexport type PaymentSummary = z.infer<typeof paymentSummarySchema>;\n\nexport const paymentPageSchema = paginatedSchema(paymentSummarySchema);\nexport type PaymentPage = z.infer<typeof paymentPageSchema>;\n\nexport const paymentDetailSchema = paymentSummarySchema.extend({\n listPrice: paiseSchema,\n discount: paiseSchema,\n coupon: z.object({ code: z.string(), label: z.string() }).nullable(),\n invoice: invoiceSchema.nullable(),\n creditNote: creditNoteSchema.nullable(),\n refundedAt: z.iso.datetime().nullable(),\n});\nexport type PaymentDetail = z.infer<typeof paymentDetailSchema>;\n\n// ─── Staff: payments and refunds ─────────────────────────────────────────────\n\n/** Who paid, as they are now, so staff can find them. Email only for a role that may read it. */\nexport const payerSchema = z.object({\n id: objectIdSchema,\n displayName: z.string(),\n email: z.string().nullable(),\n});\nexport type Payer = z.infer<typeof payerSchema>;\n\nexport const staffPaymentsQuerySchema = paginationQuerySchema.extend({\n status: orderStatusSchema.optional(),\n memberId: objectIdSchema.optional(),\n /** Only the ones a person must act on (`needsAction` below). */\n needsAction: z.preprocess(emptyStringToUndefined, z.stringbool().default(false)),\n});\nexport type StaffPaymentsQuery = z.infer<typeof staffPaymentsQuerySchema>;\nexport type StaffPaymentsQueryInput = z.input<typeof staffPaymentsQuerySchema>;\n\nexport const staffPaymentSummarySchema = paymentSummarySchema.extend({\n payer: payerSchema,\n /**\n * A person must act (0.12.0): taken but not applied and not yet refunded\n * (the automatic refund did not go through), a refund that failed at the\n * gateway, or an open chargeback.\n */\n needsAction: z.boolean(),\n});\nexport type StaffPaymentSummary = z.infer<typeof staffPaymentSummarySchema>;\nexport const staffPaymentPageSchema = paginatedSchema(staffPaymentSummarySchema);\nexport type StaffPaymentPage = z.infer<typeof staffPaymentPageSchema>;\n\nexport const staffPaymentDetailSchema = paymentDetailSchema.extend({\n payer: payerSchema,\n razorpay: z.object({\n orderId: z.string().nullable(),\n paymentId: z.string().nullable(),\n refundId: z.string().nullable(),\n }),\n /** Why staff refunded it, on record. */\n refundReason: z.string().nullable(),\n /**\n * The money was taken but what it bought could not be applied when it was\n * captured (they were a member already, the Premium time to upgrade had\n * run out, past the 3-year cap): refund it. Null when it applied.\n */\n grantFailed: z.string().nullable(),\n /** A chargeback was opened on it (cleared when it is won or closed). */\n disputedAt: z.iso.datetime().nullable(),\n /**\n * What refunds made outside Founder Circle (the Razorpay dashboard) have\n * already given back of this payment. A Refund here gives back the rest:\n * `total` less this. 0 when there were none.\n */\n refundedOutside: paiseSchema,\n /**\n * Payments captured on this order other than the one that settled it (a\n * late authorisation, a second Checkout attempt): each is refunded by\n * itself; `refunded: false` means that refund has not gone through yet and\n * the payment needs a person.\n */\n strayPayments: z.array(\n z.object({\n paymentId: z.string(),\n amount: paiseSchema,\n refunded: z.boolean(),\n at: z.iso.datetime(),\n }),\n ),\n viewer: z.object({\n /** Paid, within the window, not their own, and the asker may refund. */\n canRefund: z.boolean(),\n }),\n});\nexport type StaffPaymentDetail = z.infer<typeof staffPaymentDetailSchema>;\n\nconst reasonSchema = z.string().trim().min(BILLING_REASON_MIN).max(BILLING_REASON_MAX);\n\n/** `POST /staff/payments/:orderId/refund`. The reason is on the audit record. */\nexport const refundPaymentBodySchema = z.object({ reason: reasonSchema });\nexport type RefundPaymentBody = z.infer<typeof refundPaymentBodySchema>;\n\n/**\n * `PUT /members/:memberId/membership`: grant (a partner, a press account) or\n * revoke (a fake or duplicate account holding a founding place). A paid one\n * is never revoked: refund its payment.\n */\nexport const setMembershipBodySchema = z.object({\n granted: z.boolean(),\n reason: reasonSchema,\n});\nexport type SetMembershipBody = z.infer<typeof setMembershipBodySchema>;\n\n// ─── Staff: coupons ──────────────────────────────────────────────────────────\n\n/** What a coupon is good for. A percent coupon names what it applies to. */\nexport const COUPON_TARGET_VALUES = ['membership', 'premium', 'business'] as const;\nexport const couponTargetSchema = z.enum(COUPON_TARGET_VALUES);\nexport type CouponTarget = z.infer<typeof couponTargetSchema>;\n\n/**\n * ⚠ Stored: append only.\n *\n * free_membership the membership at Rs 0; `founding` numbers its holders\n * (\"Founding member #N\")\n * percent_off a share off the listed price of what it applies to\n */\nexport const couponBenefitSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal('free_membership'), founding: z.boolean() }),\n z.object({\n kind: z.literal('percent_off'),\n percent: z.number().int().min(1).max(90),\n appliesTo: z\n .array(couponTargetSchema)\n .min(1)\n .max(COUPON_TARGET_VALUES.length)\n .refine((list) => new Set(list).size === list.length, {\n error: VALIDATION_MESSAGE.couponTargetsDuplicate,\n }),\n }),\n]);\nexport type CouponBenefit = z.infer<typeof couponBenefitSchema>;\n\nexport const couponSchema = z.object({\n id: objectIdSchema,\n code: z.string(),\n label: z.string(),\n benefit: couponBenefitSchema,\n /** Redemptions allowed in all; null is no cap. */\n max: z.number().int().positive().nullable(),\n /** Redemptions in force (a revoked founding place frees one). */\n used: z.number().int().nonnegative(),\n active: z.boolean(),\n validFrom: z.iso.datetime().nullable(),\n validUntil: z.iso.datetime().nullable(),\n createdAt: z.iso.datetime(),\n});\nexport type Coupon = z.infer<typeof couponSchema>;\n\n/** Never paged: coupons are few. Newest first. */\nexport const couponListSchema = z.object({ items: z.array(couponSchema) });\nexport type CouponList = z.infer<typeof couponListSchema>;\n\nexport const couponParamsSchema = z.object({ couponId: objectIdSchema });\nexport type CouponParams = z.infer<typeof couponParamsSchema>;\n\nconst windowValid = (body: { validFrom?: string | null; validUntil?: string | null }) =>\n !body.validFrom || !body.validUntil || Date.parse(body.validFrom) < Date.parse(body.validUntil);\n\nexport const createCouponBodySchema = z\n .object({\n code: couponCodeSchema,\n label: z.string().trim().min(2).max(COUPON_LABEL_MAX),\n benefit: couponBenefitSchema,\n max: z.number().int().positive().nullable(),\n validFrom: z.iso.datetime().nullable().optional(),\n validUntil: z.iso.datetime().nullable().optional(),\n })\n .refine(windowValid, { path: ['validUntil'], error: VALIDATION_MESSAGE.couponWindowInvalid });\nexport type CreateCouponBody = z.infer<typeof createCouponBodySchema>;\n\n/**\n * What may change on a live coupon. The code and the benefit never change: a\n * redemption is a record of what a code gave when it was used.\n */\nexport const updateCouponBodySchema = z\n .object({\n label: z.string().trim().min(2).max(COUPON_LABEL_MAX).optional(),\n active: z.boolean().optional(),\n max: z.number().int().positive().nullable().optional(),\n validFrom: z.iso.datetime().nullable().optional(),\n validUntil: z.iso.datetime().nullable().optional(),\n })\n .refine(windowValid, { path: ['validUntil'], error: VALIDATION_MESSAGE.couponWindowInvalid });\nexport type UpdateCouponBody = z.infer<typeof updateCouponBodySchema>;\n"]}