@volter/twin-stripe 3.0.0 → 3.0.1

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 (253) hide show
  1. package/README.md +51 -21
  2. package/dist/generated/pack-facts.json +3 -3
  3. package/dist/journeys/coverage.json +596 -0
  4. package/dist/journeys/customer-life.json +6108 -0
  5. package/dist/journeys/customer-life.outline.md +371 -0
  6. package/dist/journeys/decisions.json +677 -0
  7. package/dist/journeys/demand.json +706 -0
  8. package/dist/journeys/score.json +853 -0
  9. package/dist/journeys/unreachable.json +102 -0
  10. package/dist/journeys/vendor-examples.json +8010 -0
  11. package/dist/spec/SOURCE.md +5 -1
  12. package/dist/spec/doc-examples.json +445 -0
  13. package/dist/spec/sources.json +135 -207
  14. package/dist/src/cli.d.ts +0 -1
  15. package/dist/src/cli.js +0 -2
  16. package/dist/src/engine/after-payment.d.ts +0 -1
  17. package/dist/src/engine/after-payment.js +0 -1
  18. package/dist/src/engine/common.d.ts +5 -5
  19. package/dist/src/engine/common.js +13 -11
  20. package/dist/src/engine/connect.d.ts +0 -21
  21. package/dist/src/engine/connect.js +1 -82
  22. package/dist/src/engine/customers.d.ts +0 -8
  23. package/dist/src/engine/customers.js +0 -22
  24. package/dist/src/engine/display.d.ts +0 -3
  25. package/dist/src/engine/display.js +0 -3
  26. package/dist/src/engine/files.d.ts +0 -7
  27. package/dist/src/engine/files.js +0 -10
  28. package/dist/src/engine/invoices.d.ts +6 -9
  29. package/dist/src/engine/invoices.js +56 -29
  30. package/dist/src/engine/issuing.d.ts +6 -8
  31. package/dist/src/engine/issuing.js +19 -10
  32. package/dist/src/engine/ledger.d.ts +0 -1
  33. package/dist/src/engine/ledger.js +0 -1
  34. package/dist/src/engine/oauth.js +1 -2
  35. package/dist/src/engine/refunds.js +2 -27
  36. package/dist/src/engine/stripe.d.ts +2 -7
  37. package/dist/src/engine/stripe.js +34 -83
  38. package/dist/src/engine/subscriptions.d.ts +0 -1
  39. package/dist/src/engine/subscriptions.js +0 -1
  40. package/dist/src/engine/version.d.ts +0 -2
  41. package/dist/src/engine/version.js +17 -6
  42. package/dist/src/fetch.js +2 -2
  43. package/dist/src/generated/surface.gen.json +1 -1
  44. package/dist/src/generated/ui.gen.json +1 -1
  45. package/dist/src/index.js +5 -3
  46. package/dist/src/manifest.js +348 -220
  47. package/dist/src/screens/billing-portal.js +5 -5
  48. package/dist/src/screens/{connect-oauth.d.ts → checkout-js.d.ts} +0 -1
  49. package/dist/src/screens/checkout-js.js +84 -0
  50. package/dist/src/screens/connect-onboarding.js +3 -3
  51. package/dist/src/screens/dashboard.d.ts +1 -1
  52. package/dist/src/screens/dashboard.js +52 -53
  53. package/dist/src/screens/stripe-js.js +53 -93
  54. package/dist/src/semantics/account.d.ts +2 -0
  55. package/dist/src/semantics/account.js +8 -0
  56. package/dist/src/semantics/accounts.js +3 -1
  57. package/dist/src/semantics/around.js +3 -6
  58. package/dist/src/semantics/billing_portal.d.ts +0 -1
  59. package/dist/src/semantics/billing_portal.js +1 -7
  60. package/dist/src/semantics/checkout.d.ts +1 -1
  61. package/dist/src/semantics/checkout.js +18 -15
  62. package/dist/src/semantics/clock.js +1 -7
  63. package/dist/src/semantics/customers.d.ts +1 -2
  64. package/dist/src/semantics/customers.js +32 -38
  65. package/dist/src/semantics/entitlements.d.ts +0 -2
  66. package/dist/src/semantics/entitlements.js +4 -50
  67. package/dist/src/semantics/events.d.ts +2 -3
  68. package/dist/src/semantics/events.js +6 -11
  69. package/dist/src/semantics/index.js +16 -38
  70. package/dist/src/semantics/invoices.d.ts +3 -8
  71. package/dist/src/semantics/invoices.js +134 -127
  72. package/dist/src/semantics/issuing.d.ts +6 -0
  73. package/dist/src/semantics/issuing.js +77 -0
  74. package/dist/src/semantics/payment_intents.d.ts +0 -1
  75. package/dist/src/semantics/payment_intents.js +5 -13
  76. package/dist/src/semantics/payment_methods.d.ts +0 -2
  77. package/dist/src/semantics/payment_methods.js +2 -26
  78. package/dist/src/semantics/payouts.d.ts +1 -0
  79. package/dist/src/semantics/payouts.js +8 -1
  80. package/dist/src/semantics/products.d.ts +1 -3
  81. package/dist/src/semantics/products.js +3 -14
  82. package/dist/src/semantics/refunds.d.ts +1 -2
  83. package/dist/src/semantics/refunds.js +1 -5
  84. package/dist/src/semantics/shared.d.ts +64 -336
  85. package/dist/src/semantics/shared.js +326 -1336
  86. package/dist/src/semantics/states.d.ts +20 -89
  87. package/dist/src/semantics/states.js +116 -334
  88. package/dist/src/semantics/subscriptions.d.ts +1 -2
  89. package/dist/src/semantics/subscriptions.js +23 -145
  90. package/dist/src/semantics/test_helpers.d.ts +2 -5
  91. package/dist/src/semantics/test_helpers.js +121 -43
  92. package/dist/src/semantics/webhook_endpoints.d.ts +2 -0
  93. package/dist/src/semantics/webhook_endpoints.js +26 -0
  94. package/generated/pack-facts.json +3 -3
  95. package/journeys/coverage.json +596 -0
  96. package/journeys/customer-life.json +6108 -0
  97. package/journeys/customer-life.outline.md +371 -0
  98. package/journeys/decisions.json +677 -0
  99. package/journeys/demand.json +706 -0
  100. package/journeys/score.json +853 -0
  101. package/journeys/unreachable.json +102 -0
  102. package/journeys/vendor-examples.json +8010 -0
  103. package/package.json +4 -3
  104. package/spec/SOURCE.md +5 -1
  105. package/spec/doc-examples.json +445 -0
  106. package/spec/sources.json +135 -207
  107. package/src/cli.ts +0 -3
  108. package/src/engine/after-payment.ts +0 -2
  109. package/src/engine/balance.ts +0 -1
  110. package/src/engine/checkout.ts +0 -1
  111. package/src/engine/common.ts +19 -17
  112. package/src/engine/connect.ts +1 -79
  113. package/src/engine/customers.ts +0 -25
  114. package/src/engine/display.ts +2 -3
  115. package/src/engine/files.ts +0 -13
  116. package/src/engine/invoices.ts +64 -33
  117. package/src/engine/issuing.ts +16 -11
  118. package/src/engine/ledger.ts +0 -2
  119. package/src/engine/oauth.ts +1 -2
  120. package/src/engine/onboarding.ts +0 -1
  121. package/src/engine/params.ts +0 -1
  122. package/src/engine/payment-intents.ts +0 -1
  123. package/src/engine/payment-methods.ts +0 -1
  124. package/src/engine/refunds.ts +2 -28
  125. package/src/engine/renewals.ts +0 -1
  126. package/src/engine/stripe.ts +35 -83
  127. package/src/engine/subscriptions.ts +0 -3
  128. package/src/engine/transfers.ts +0 -1
  129. package/src/engine/version.ts +15 -7
  130. package/src/engine/webhooks.ts +0 -2
  131. package/src/fetch.ts +2 -2
  132. package/src/generated/surface.gen.json +1 -1
  133. package/src/generated/ui.gen.json +1 -1
  134. package/src/index.ts +6 -4
  135. package/src/manifest.ts +348 -220
  136. package/src/screens/billing-portal.tsx +5 -7
  137. package/src/screens/checkout-js.ts +80 -0
  138. package/src/screens/checkout.tsx +0 -2
  139. package/src/screens/connect-onboarding.tsx +3 -5
  140. package/src/screens/dashboard.tsx +53 -73
  141. package/src/screens/stripe-js.ts +57 -125
  142. package/src/semantics/account.ts +10 -0
  143. package/src/semantics/account_links.ts +0 -1
  144. package/src/semantics/accounts.ts +8 -7
  145. package/src/semantics/apps.ts +1 -2
  146. package/src/semantics/around.ts +3 -6
  147. package/src/semantics/balance.ts +0 -1
  148. package/src/semantics/balance_transactions.ts +1 -3
  149. package/src/semantics/billing_portal.ts +1 -7
  150. package/src/semantics/charges.ts +1 -6
  151. package/src/semantics/checkout.ts +16 -12
  152. package/src/semantics/clock.ts +1 -8
  153. package/src/semantics/coupons.ts +0 -1
  154. package/src/semantics/customers.ts +32 -35
  155. package/src/semantics/doors.ts +2 -3
  156. package/src/semantics/entitlements.ts +1 -45
  157. package/src/semantics/events.ts +7 -14
  158. package/src/semantics/index.ts +16 -38
  159. package/src/semantics/invoice_payments.ts +1 -3
  160. package/src/semantics/invoices.ts +124 -119
  161. package/src/semantics/issuing.ts +74 -0
  162. package/src/semantics/payment_intents.ts +10 -17
  163. package/src/semantics/payment_methods.ts +2 -25
  164. package/src/semantics/payouts.ts +9 -3
  165. package/src/semantics/prices.ts +0 -1
  166. package/src/semantics/products.ts +4 -14
  167. package/src/semantics/promotion_codes.ts +0 -1
  168. package/src/semantics/radar.ts +1 -3
  169. package/src/semantics/refunds.ts +2 -11
  170. package/src/semantics/shared.ts +350 -1382
  171. package/src/semantics/states.ts +109 -330
  172. package/src/semantics/subscriptions.ts +25 -145
  173. package/src/semantics/test_helpers.ts +109 -43
  174. package/src/semantics/transfers.ts +1 -3
  175. package/src/semantics/webhook_endpoints.ts +27 -0
  176. package/src/server.ts +0 -1
  177. package/dist/src/engine/billing.d.ts +0 -5
  178. package/dist/src/engine/billing.js +0 -6
  179. package/dist/src/engine/charges.d.ts +0 -6
  180. package/dist/src/engine/charges.js +0 -13
  181. package/dist/src/engine/connect-settings.d.ts +0 -16
  182. package/dist/src/engine/connect-settings.js +0 -26
  183. package/dist/src/engine/coupons.d.ts +0 -0
  184. package/dist/src/engine/coupons.js +0 -5
  185. package/dist/src/engine/credit-notes.d.ts +0 -1
  186. package/dist/src/engine/credit-notes.js +0 -5
  187. package/dist/src/engine/disputes.d.ts +0 -0
  188. package/dist/src/engine/disputes.js +0 -9
  189. package/dist/src/engine/entitlements.d.ts +0 -1
  190. package/dist/src/engine/entitlements.js +0 -4
  191. package/dist/src/engine/ephemeral-keys.d.ts +0 -0
  192. package/dist/src/engine/ephemeral-keys.js +0 -4
  193. package/dist/src/engine/front.d.ts +0 -5
  194. package/dist/src/engine/front.js +0 -25
  195. package/dist/src/engine/payment-links.d.ts +0 -1
  196. package/dist/src/engine/payment-links.js +0 -5
  197. package/dist/src/engine/plans.d.ts +0 -6
  198. package/dist/src/engine/plans.js +0 -26
  199. package/dist/src/engine/platform.d.ts +0 -39
  200. package/dist/src/engine/platform.js +0 -34
  201. package/dist/src/engine/products.d.ts +0 -0
  202. package/dist/src/engine/products.js +0 -4
  203. package/dist/src/engine/setup-intents.d.ts +0 -1
  204. package/dist/src/engine/setup-intents.js +0 -4
  205. package/dist/src/engine/subscription-schedules.d.ts +0 -3
  206. package/dist/src/engine/subscription-schedules.js +0 -1
  207. package/dist/src/engine/tax.d.ts +0 -7
  208. package/dist/src/engine/tax.js +0 -28
  209. package/dist/src/engine/terminal.d.ts +0 -4
  210. package/dist/src/engine/terminal.js +0 -20
  211. package/dist/src/engine/test-clocks.d.ts +0 -5
  212. package/dist/src/engine/test-clocks.js +0 -9
  213. package/dist/src/engine/tokens.d.ts +0 -7
  214. package/dist/src/engine/tokens.js +0 -20
  215. package/dist/src/engine/treasury.d.ts +0 -23
  216. package/dist/src/engine/treasury.js +0 -45
  217. package/dist/src/screens/connect-oauth.js +0 -333
  218. package/dist/src/semantics/disputes.d.ts +0 -2
  219. package/dist/src/semantics/disputes.js +0 -5
  220. package/dist/src/semantics/invoiceitems.d.ts +0 -3
  221. package/dist/src/semantics/invoiceitems.js +0 -59
  222. package/dist/src/semantics/setup_intents.d.ts +0 -3
  223. package/dist/src/semantics/setup_intents.js +0 -41
  224. package/dist/src/semantics/subscription_items.d.ts +0 -5
  225. package/dist/src/semantics/subscription_items.js +0 -55
  226. package/dist/src/semantics/subscription_schedules.d.ts +0 -4
  227. package/dist/src/semantics/subscription_schedules.js +0 -60
  228. package/src/engine/billing.ts +0 -16
  229. package/src/engine/charges.ts +0 -19
  230. package/src/engine/connect-settings.ts +0 -23
  231. package/src/engine/coupons.ts +0 -5
  232. package/src/engine/credit-notes.ts +0 -6
  233. package/src/engine/disputes.ts +0 -9
  234. package/src/engine/entitlements.ts +0 -5
  235. package/src/engine/ephemeral-keys.ts +0 -3
  236. package/src/engine/front.ts +0 -31
  237. package/src/engine/payment-links.ts +0 -6
  238. package/src/engine/plans.ts +0 -42
  239. package/src/engine/platform.ts +0 -47
  240. package/src/engine/products.ts +0 -3
  241. package/src/engine/setup-intents.ts +0 -5
  242. package/src/engine/subscription-schedules.ts +0 -10
  243. package/src/engine/tax.ts +0 -39
  244. package/src/engine/terminal.ts +0 -23
  245. package/src/engine/test-clocks.ts +0 -11
  246. package/src/engine/tokens.ts +0 -22
  247. package/src/engine/treasury.ts +0 -47
  248. package/src/screens/connect-oauth.tsx +0 -376
  249. package/src/semantics/disputes.ts +0 -12
  250. package/src/semantics/invoiceitems.ts +0 -59
  251. package/src/semantics/setup_intents.ts +0 -45
  252. package/src/semantics/subscription_items.ts +0 -54
  253. package/src/semantics/subscription_schedules.ts +0 -66
@@ -1,6 +1,15 @@
1
1
  import { CS, sessionMethodTypes } from "../engine/checkout.js";
2
- import { applyCouponDiscount, lineItemEntries, nowUnix, resolveLineItem } from "../engine/stripe.js";
3
- import { actingAccount, at, checkoutNoDestination, created, discountOf, fail, finder, noLineItems, sessionMissing, sessionTrialRefused } from "./shared.js";
2
+ import { applyCouponDiscount, lineItemEntries, nowUnix, paginate, resolveLineItem } from "../engine/stripe.js";
3
+ import { actingAccount, at, checkoutNoDestination, created, discountOf, fail, finder, noLineItems, path, sessionMissing, sessionTrialRefused } from "./shared.js";
4
+ export async function GetCheckoutSessionsSessionLineItems(ctx) {
5
+ const s = ctx.get(CS, at(ctx, 'session'));
6
+ if (!s)
7
+ return sessionMissing(ctx, at(ctx, 'session'));
8
+ // source: spec:GetCheckoutSessionsSessionLineItems "There is also a URL where you can retrieve the full (paginated) list of line items."
9
+ const li = s.line_items;
10
+ const { page, hasMore } = paginate(Array.isArray(li?.data) ? li.data : [], ctx.params);
11
+ return ctx.reply({ object: 'list', url: path(ctx), has_more: hasMore, data: page });
12
+ }
4
13
  export async function PostCheckoutSessions(ctx) {
5
14
  const params = ctx.params;
6
15
  const mode = typeof params.mode === 'string' ? params.mode : 'payment';
@@ -9,8 +18,9 @@ export async function PostCheckoutSessions(ctx) {
9
18
  const entries = lineItemEntries(params);
10
19
  if (mode !== 'setup' && entries.length === 0)
11
20
  return noLineItems(ctx);
12
- // hosted Checkout (the only ui_mode the twin models) needs success_url
13
- if (params.success_url === undefined || params.success_url === '')
21
+ // source: spec:/paths/~1v1~1checkout~1sessions/post/requestBody/content/application~1x-www-form-urlencoded/schema/properties/ui_mode "The UI mode of the Session."
22
+ const custom = params.ui_mode === 'custom';
23
+ if (!custom && (params.success_url === undefined || params.success_url === ''))
14
24
  return fail(ctx, 'Missing required param: success_url.', 400, 'parameter_missing');
15
25
  const sessionCurrency = typeof params.currency === 'string' ? params.currency : 'usd';
16
26
  const resolved = entries.map((e, i) => resolveLineItem(e, i, sessionCurrency, finder(ctx)));
@@ -32,7 +42,10 @@ export async function PostCheckoutSessions(ctx) {
32
42
  const fields = {
33
43
  mode, status: 'open',
34
44
  payment_status: mode === 'setup' ? 'no_payment_required' : 'unpaid',
35
- url: `https://checkout.stripe.com/c/pay/${id}`, success_url: params.success_url ?? null, cancel_url: params.cancel_url ?? null,
45
+ ui_mode: custom ? 'custom' : 'hosted',
46
+ ...(custom ? { client_secret: `${id}_secret_${await ctx.secret(`checkout-secret:${id}`)}`, return_url: params.return_url ?? null } : {}),
47
+ allow_promotion_codes: params.allow_promotion_codes === true || params.allow_promotion_codes === 'true',
48
+ url: custom ? null : `https://checkout.stripe.com/c/pay/${id}`, success_url: params.success_url ?? null, cancel_url: params.cancel_url ?? null,
36
49
  customer: params.customer ?? null, client_reference_id: params.client_reference_id ?? null,
37
50
  customer_email: params.customer_email ?? null,
38
51
  // whether paying makes a customer: in payment mode only when asked (`always`), `if_required` by default
@@ -74,13 +87,3 @@ export async function PostCheckoutSessions(ctx) {
74
87
  fields._discount = { coupon: String(discount.coupon.id), promotion_code: discount.promotion ?? null };
75
88
  return ctx.reply(await created(ctx, CS, { id, ...fields }, {}));
76
89
  }
77
- export async function PostCheckoutSessionsSessionExpire(ctx) {
78
- const id = at(ctx, 'session');
79
- const s = ctx.get(CS, id);
80
- if (!s)
81
- return sessionMissing(ctx, id);
82
- const refused = ctx.legal(CS, 'status', 'PostCheckoutSessionsSessionExpire', s.status, undefined, id);
83
- if (refused)
84
- return ctx.refuse(refused);
85
- return ctx.reply(await ctx.write(CS, id, { status: 'expired', url: null }, 'checkout.session.expire'));
86
- }
@@ -1,17 +1,11 @@
1
- import { advanceBilling, advancePayouts, finishClockAdvances, finishReportRuns, lapseCoupons, lapseRealtimeRequests, settleBankDebits, settleHeldRefunds, settleTopups } from "./shared.js";
1
+ import { advanceBilling, advancePayouts, lapseCoupons, lapseRealtimeRequests, settleBankDebits, settleHeldRefunds } from "./shared.js";
2
2
  export async function clock(ctx) {
3
- // a test clock advanced by the last request has reached its time
4
- await finishClockAdvances(ctx);
5
3
  // a subscription renews at its period's end, its renewal charged an hour later
6
4
  await advanceBilling(ctx);
7
5
  // a submitted bank debit settles (in test mode at once)
8
6
  await settleBankDebits(ctx);
9
7
  // a coupon past its redeem_by is no longer valid
10
8
  await lapseCoupons(ctx);
11
- // a top-up's funds arrive five days after it is made
12
- await settleTopups(ctx);
13
- // a report run completes
14
- await finishReportRuns(ctx);
15
9
  // a real-time authorization request no one answered is decided when its window ends
16
10
  await lapseRealtimeRequests(ctx);
17
11
  // a refund held for want of balance is made when funds cover it, before any payout takes them
@@ -1,6 +1,5 @@
1
1
  import type { HandlerContext } from '@volter/world-core';
2
2
  export declare function GetCustomersCustomer(ctx: HandlerContext): Promise<Response>;
3
- export declare function GetCustomersCustomerPaymentMethods(ctx: HandlerContext): Promise<Response>;
4
3
  export declare function GetCustomersSearch(ctx: HandlerContext): Promise<Response>;
5
4
  export declare function PostCustomers(ctx: HandlerContext): Promise<Response>;
6
- export declare function PostCustomersCustomerBalanceTransactions(ctx: HandlerContext): Promise<Response>;
5
+ export declare function PostCustomersCustomer(ctx: HandlerContext): Promise<Response>;
@@ -1,5 +1,6 @@
1
1
  import { newCustomer } from "../engine/customers.js";
2
- import { at, attachable, attachPaymentMethod, created, customerDiscount, customerMissing, expanded, fail, list, newest, redeemCoupon, search, where } from "./shared.js";
2
+ import { nowUnix } from "../engine/stripe.js";
3
+ import { at, attachable, attachPaymentMethod, created, customerMissing, expanded, fail, search } from "./shared.js";
3
4
  // "If it’s for a deleted Customer, a subset of the customer’s information is returned, including a `deleted` property
4
5
  // that’s set to true" (docs.stripe.com/api/customers/retrieve)
5
6
  export async function GetCustomersCustomer(ctx) {
@@ -11,23 +12,10 @@ export async function GetCustomersCustomer(ctx) {
11
12
  return ctx.reply({ id, object: 'customer', deleted: true });
12
13
  return ctx.reply(expanded(ctx, 'customer', ctx.get('customer', id)));
13
14
  }
14
- // the payment methods attached to a customer (customers.listPaymentMethods), optionally by `type`
15
- export async function GetCustomersCustomerPaymentMethods(ctx) {
16
- const customer = at(ctx, 'customer');
17
- if (!ctx.get('customer', customer))
18
- return customerMissing(ctx, customer);
19
- const items = where(ctx, newest(ctx, 'payment_method').filter((m) => m.customer === customer), { type: (m, v) => m.type === v });
20
- return list(ctx, 'payment_method', items);
21
- }
22
15
  export async function GetCustomersSearch(ctx) {
23
16
  return search(ctx, 'customer');
24
17
  }
25
18
  export async function PostCustomers(ctx) {
26
- // a `coupon` on create materializes the discount object on the customer (the coupon must exist)
27
- const couponId = typeof ctx.params.coupon === 'string' ? ctx.params.coupon : '';
28
- const discount = couponId ? customerDiscount(ctx, couponId) : null;
29
- if (discount instanceof Response)
30
- return discount;
31
19
  // `payment_method` attaches that method to the new customer; it is not a field of the customer
32
20
  // (docs.stripe.com/api/customers/create#create_customer-payment_method)
33
21
  const pmId = typeof ctx.params.payment_method === 'string' ? ctx.params.payment_method : '';
@@ -36,34 +24,40 @@ export async function PostCustomers(ctx) {
36
24
  const given = ctx.params;
37
25
  const { coupon: _c, payment_method: _pm, ...rest } = given;
38
26
  const id = ctx.mint('customer');
39
- const customer = await created(ctx, 'customer', { ...rest, id }, { livemode: false, discount: discount ? { ...discount, customer: id } : null, ...newCustomer(id) });
40
- if (discount)
41
- await redeemCoupon(ctx, couponId);
27
+ const customer = await created(ctx, 'customer', { ...rest, id }, { livemode: false, discount: null, ...newCustomer(id) });
28
+ // source: https://docs.stripe.com/billing/customer/balance "Represents the starting value of the customer invoice balance when a customer is created using the API with a non-zero invoice balance."
29
+ if (Number(customer.balance ?? 0) !== 0) {
30
+ await created(ctx, 'customer_balance_transaction', {
31
+ customer: id, amount: Number(customer.balance), ending_balance: Number(customer.balance),
32
+ currency: customer.currency ?? 'usd', type: 'initial', description: null, metadata: {},
33
+ livemode: false, invoice: null, credit_note: null, checkout_session: null, customer_account: null,
34
+ }, {});
35
+ }
42
36
  if (pmId)
43
37
  await attachPaymentMethod(ctx, pmId, String(customer.id));
44
38
  return ctx.reply(customer);
45
39
  }
46
40
  // ── the credit-balance ledger: a negative amount is a credit; `ending_balance` is the running sum,
47
41
  // mirrored onto the customer's `balance` ──
48
- export async function PostCustomersCustomerBalanceTransactions(ctx) {
49
- const customer = at(ctx, 'customer');
50
- if (!ctx.get('customer', customer))
51
- return customerMissing(ctx, customer);
52
- const params = ctx.params;
53
- if (params.amount === undefined)
54
- return fail(ctx, 'Missing required param: amount.', 400, 'parameter_missing');
55
- const amount = Number(params.amount);
56
- if (!Number.isInteger(amount))
57
- return fail(ctx, 'Invalid integer: amount.', 400, 'parameter_invalid_integer');
58
- if (params.currency === undefined || params.currency === '')
59
- return fail(ctx, 'Missing required param: currency.', 400, 'parameter_missing');
60
- const prior = ctx.rows('customer_balance_transaction').filter((t) => t.customer === customer).reduce((s, t) => s + (Number(t.amount) || 0), 0);
61
- const ending = prior + amount;
62
- const body = await created(ctx, 'customer_balance_transaction', { ...params, customer, amount }, {
63
- ending_balance: ending, type: 'adjustment', livemode: false, metadata: {},
64
- description: params.description ?? null, credit_note: null, invoice: null, checkout_session: null,
65
- });
66
- // keyed by the new transaction so two adjustments at one instant are two writes, the last winning
67
- await ctx.write('customer', customer, { balance: ending }, `customer.balance_adjusted:${String(body.id)}`);
68
- return ctx.reply(body);
42
+ // source: spec:PostCustomersCustomer "Updates the specified customer by setting the values of the parameters passed."
43
+ export async function PostCustomersCustomer(ctx) {
44
+ const id = at(ctx, 'customer');
45
+ const customer = ctx.get('customer', id);
46
+ if (!customer)
47
+ return customerMissing(ctx, id);
48
+ const { customer: _pathCustomer, ...fields } = ctx.params;
49
+ // source: https://docs.stripe.com/billing/customer/balance "All modifications to the invoice balance are recorded as Transactions"
50
+ // source: https://docs.stripe.com/billing/customer/balance "All Transactions created with the API or in the Dashboard have a type value of adjustment"
51
+ if (typeof fields.balance === 'number' && fields.balance !== Number(customer.balance ?? 0)) {
52
+ const balance = fields.balance;
53
+ await created(ctx, 'customer_balance_transaction', {
54
+ customer: id, amount: balance - Number(customer.balance ?? 0), ending_balance: balance,
55
+ // Stripe inherits the customer's billing currency. Where no invoice has set it, this World's invoice currency is USD.
56
+ currency: customer.currency ?? 'usd', type: 'adjustment',
57
+ description: null, metadata: {}, livemode: false,
58
+ invoice: null, credit_note: null, checkout_session: null, customer_account: null,
59
+ created: nowUnix(ctx.occurredAt),
60
+ }, {});
61
+ }
62
+ return ctx.reply(ctx.expand('customer', await ctx.write('customer', id, fields, 'customer.update')));
69
63
  }
@@ -1,2 +0,0 @@
1
- import type { HandlerContext } from '@volter/world-core';
2
- export declare function GetEntitlementsActiveEntitlements(ctx: HandlerContext): Promise<Response>;
@@ -1,50 +1,4 @@
1
- import { FEAT } from "../engine/entitlements.js";
2
- import { subscriptionItemPairs } from "../engine/stripe.js";
3
- import { fail, newest, path } from "./shared.js";
4
- // a customer is entitled to every feature granted to a product its live subscriptions bill, as the product stood when
5
- // the subscription's current period began: "When a customer purchases or subscribes to a product with an attached
6
- // feature, Stripe creates and manages an Active Entitlement for that customer and feature"; "Existing subscriptions will
7
- // create active entitlements for any product feature changes at the start of the next billing period"
8
- // (docs.stripe.com/billing/entitlements). Where the documentation stops and the twin decides: a feature removed from a
9
- // product is kept, the same way, until the next period begins.
10
- export async function GetEntitlementsActiveEntitlements(ctx) {
11
- const customer = typeof ctx.params.customer === 'string' ? ctx.params.customer : '';
12
- if (!customer)
13
- return fail(ctx, 'Missing required param: customer.', 400, 'parameter_missing');
14
- if (!ctx.row('customer', customer, { withDeleted: true }))
15
- return fail(ctx, `No such customer: '${customer}'`, 404, 'resource_missing');
16
- // each product a live subscription bills, with the start of the period it is billed for
17
- const productIds = new Map();
18
- for (const sub of newest(ctx, 'subscription')) {
19
- // a subscription entitles while it runs: not before its first payment (incomplete), nor once it has ended
20
- // (docs.stripe.com/billing/entitlements; past_due keeps access during retries — that edge is the twin's reading)
21
- if (sub.customer !== customer || !['active', 'trialing', 'past_due'].includes(String(sub.status)))
22
- continue;
23
- const periodStart = Number(sub.current_period_start ?? sub.start_date ?? sub.created ?? 0);
24
- for (const it of subscriptionItemPairs(sub)) {
25
- const price = ctx.get('price', it.price);
26
- if (price && typeof price.product === 'string')
27
- productIds.set(price.product, Math.max(productIds.get(price.product) ?? -Infinity, periodStart));
28
- }
29
- }
30
- const seen = new Set();
31
- const data = [];
32
- for (const pf of ctx.rowsRaw('product_feature', { withDeleted: true })) {
33
- const periodStart = productIds.get(String(pf._product));
34
- if (periodStart === undefined)
35
- continue;
36
- // attached by the period's start, and not removed before it
37
- if (Number(pf._created ?? 0) > periodStart)
38
- continue;
39
- if (pf.deleted && !(Number(pf._removed_at) > periodStart))
40
- continue;
41
- const featId = String(pf._feature ?? pf.entitlement_feature?.id ?? pf.entitlement_feature);
42
- if (seen.has(featId))
43
- continue;
44
- seen.add(featId);
45
- const feat = ctx.get(FEAT, featId);
46
- if (feat)
47
- data.push({ id: `ent_twin_${customer}_${featId}`, object: 'entitlements.active_entitlement', feature: featId, lookup_key: feat.lookup_key, livemode: false });
48
- }
49
- return ctx.reply({ object: 'list', url: path(ctx), has_more: false, data });
50
- }
1
+ "use strict";
2
+ // Stripe's entitlements operations (docs/contributing/architecture.md, "What an author writes"): each handler over the
3
+ // handler contract's context, computing with the engine (../engine/).
4
+ // source: spec:PostEntitlementsFeatures "Creates a feature"
@@ -1,7 +1,6 @@
1
1
  import type { EventWrite, WriteHookContext } from '@volter/world-core';
2
- /** An event's `data`: the object the write concerns, as Stripe sends it in the event's API version. A plan is stored as
3
- * the recurring price it is, and its own events carry the plan object; an Issuing authorization carries its full card;
4
- * the balance notice the drain writes carries the account's Balance. */
2
+ /** An event's `data`: the object the write concerns, as Stripe sends it in the event's API version. An Issuing
3
+ * authorization carries its full card; the balance notice the drain writes carries the account's Balance. */
5
4
  export declare function data(ctx: WriteHookContext, write: EventWrite): Record<string, unknown>;
6
5
  /** An event's further envelope values: the connected account it is about ("Each event for a connected account contains
7
6
  * a top-level `account` property that identifies the connected account", docs.stripe.com/connect/webhooks) — the
@@ -1,16 +1,12 @@
1
- import { planOf } from "../engine/plans.js";
2
- import { embedIssuingAuthorizationCard, isValidApiVersion, PLATFORM_ACCOUNT_ID, TWIN_API_VERSION } from "../engine/stripe.js";
1
+ import { isValidApiVersion, PLATFORM_ACCOUNT_ID, TWIN_API_VERSION } from "../engine/stripe.js";
3
2
  import { render } from "../engine/version.js";
4
- import { connectionRevoked, finder } from "./shared.js";
5
3
  // ── the events Stripe's writes send (the manifest's `events`: ./shared.ts STRIPE_EVENTS) ──
6
- /** An event's `data`: the object the write concerns, as Stripe sends it in the event's API version. A plan is stored as
7
- * the recurring price it is, and its own events carry the plan object; an Issuing authorization carries its full card;
8
- * the balance notice the drain writes carries the account's Balance. */
4
+ /** An event's `data`: the object the write concerns, as Stripe sends it in the event's API version. An Issuing
5
+ * authorization carries its full card; the balance notice the drain writes carries the account's Balance. */
9
6
  export function data(ctx, write) {
10
7
  const object = write.storedType === '_balance_notice' ? write.body.balance
11
- : write.storedType === 'price' && write.operation.startsWith('plan.') ? planOf(write.body)
12
- : write.storedType === 'issuing_authorization' ? embedIssuingAuthorizationCard({ ...write.body }, finder(ctx))
13
- : write.body;
8
+ : write.storedType === 'issuing_authorization' ? ctx.expand('issuing.authorization', write.body)
9
+ : write.storedType === 'issuing_card' ? ctx.expand('issuing.card', write.body) : ctx.expand(String(write.body.object ?? write.storedType), write.body);
14
10
  return { object: render(object, eventVersion(write)) };
15
11
  }
16
12
  /** An event's further envelope values: the connected account it is about ("Each event for a connected account contains
@@ -26,8 +22,7 @@ export function values(ctx, write, type) {
26
22
  ?? (write.storedType === 'account' && typeof id === 'string' && id !== PLATFORM_ACCOUNT_ID ? id : undefined)
27
23
  ?? (write.storedType === '_balance_notice' && typeof write.body.account === 'string' ? write.body.account : undefined)
28
24
  ?? (typeof kept === 'string' ? kept : undefined);
29
- const withheld = account !== undefined && type !== 'account.application.deauthorized' && connectionRevoked(ctx, account);
30
- return { $account: account, $version: eventVersion(write), ...(withheld ? { $send: false } : {}) };
25
+ return { $account: account, $version: eventVersion(write) };
31
26
  }
32
27
  /** The API version an event is rendered in: the one the writing request pinned, when it is one, else the served one. */
33
28
  const eventVersion = (write) => {
@@ -1,3 +1,4 @@
1
+ import * as account from "./account.js";
1
2
  import * as account_links from "./account_links.js";
2
3
  import * as accounts from "./accounts.js";
3
4
  import * as apps from "./apps.js";
@@ -8,11 +9,9 @@ import * as charges from "./charges.js";
8
9
  import * as checkout from "./checkout.js";
9
10
  import * as coupons from "./coupons.js";
10
11
  import * as customers from "./customers.js";
11
- import * as disputes from "./disputes.js";
12
- import * as entitlements from "./entitlements.js";
13
12
  import * as invoice_payments from "./invoice_payments.js";
14
- import * as invoiceitems from "./invoiceitems.js";
15
13
  import * as invoices from "./invoices.js";
14
+ import * as issuing from "./issuing.js";
16
15
  import * as payment_intents from "./payment_intents.js";
17
16
  import * as payment_methods from "./payment_methods.js";
18
17
  import * as payouts from "./payouts.js";
@@ -21,19 +20,14 @@ import * as products from "./products.js";
21
20
  import * as promotion_codes from "./promotion_codes.js";
22
21
  import * as radar from "./radar.js";
23
22
  import * as refunds from "./refunds.js";
24
- import * as setup_intents from "./setup_intents.js";
25
- import * as subscription_items from "./subscription_items.js";
26
- import * as subscription_schedules from "./subscription_schedules.js";
27
23
  import * as subscriptions from "./subscriptions.js";
28
24
  import * as test_helpers from "./test_helpers.js";
29
25
  import * as transfers from "./transfers.js";
26
+ import * as webhook_endpoints from "./webhook_endpoints.js";
30
27
  export const stripeSemantics = {
31
- DeleteInvoiceitemsInvoiceitem: invoiceitems.DeleteInvoiceitemsInvoiceitem,
32
- DeleteInvoicesInvoice: invoices.DeleteInvoicesInvoice,
33
- DeleteProductsId: products.DeleteProductsId,
34
- DeleteSubscriptionItemsItem: subscription_items.DeleteSubscriptionItemsItem,
35
28
  DeleteSubscriptionsSubscriptionExposedId: subscriptions.DeleteSubscriptionsSubscriptionExposedId,
36
- DeleteTestHelpersTestClocksTestClock: test_helpers.DeleteTestHelpersTestClocksTestClock,
29
+ DeleteSubscriptionsSubscriptionExposedIdDiscount: subscriptions.DeleteSubscriptionsSubscriptionExposedIdDiscount,
30
+ GetAccount: account.GetAccount,
37
31
  GetAccountsAccountExternalAccounts: accounts.GetAccountsAccountExternalAccounts,
38
32
  GetAppsSecretsFind: apps.GetAppsSecretsFind,
39
33
  GetBalance: balance.GetBalance,
@@ -41,25 +35,19 @@ export const stripeSemantics = {
41
35
  GetBillingPortalConfigurations: billing_portal.GetBillingPortalConfigurations,
42
36
  GetCharges: charges.GetCharges,
43
37
  GetChargesCharge: charges.GetChargesCharge,
38
+ GetCheckoutSessionsSessionLineItems: checkout.GetCheckoutSessionsSessionLineItems,
44
39
  GetCustomersCustomer: customers.GetCustomersCustomer,
45
- GetCustomersCustomerPaymentMethods: customers.GetCustomersCustomerPaymentMethods,
46
40
  GetCustomersSearch: customers.GetCustomersSearch,
47
- GetDisputesDispute: disputes.GetDisputesDispute,
48
- GetEntitlementsActiveEntitlements: entitlements.GetEntitlementsActiveEntitlements,
49
41
  GetInvoicePayments: invoice_payments.GetInvoicePayments,
50
42
  GetInvoicesInvoice: invoices.GetInvoicesInvoice,
51
- GetInvoicesInvoiceLines: invoices.GetInvoicesInvoiceLines,
43
+ GetIssuingCardsCard: issuing.GetIssuingCardsCard,
52
44
  GetPaymentMethodsPaymentMethod: payment_methods.GetPaymentMethodsPaymentMethod,
45
+ GetPayouts: payouts.GetPayouts,
53
46
  GetPrices: prices.GetPrices,
54
47
  GetPricesPrice: prices.GetPricesPrice,
55
48
  GetProducts: products.GetProducts,
56
- GetProductsSearch: products.GetProductsSearch,
57
49
  GetPromotionCodes: promotion_codes.GetPromotionCodes,
58
- GetRefundsRefund: refunds.GetRefundsRefund,
59
- GetSubscriptionItemsItem: subscription_items.GetSubscriptionItemsItem,
60
- GetSubscriptionSchedulesSchedule: subscription_schedules.GetSubscriptionSchedulesSchedule,
61
50
  GetSubscriptions: subscriptions.GetSubscriptions,
62
- GetSubscriptionsSearch: subscriptions.GetSubscriptionsSearch,
63
51
  GetSubscriptionsSubscriptionExposedId: subscriptions.GetSubscriptionsSubscriptionExposedId,
64
52
  PostAccountLinks: account_links.PostAccountLinks,
65
53
  PostAccounts: accounts.PostAccounts,
@@ -67,25 +55,21 @@ export const stripeSemantics = {
67
55
  PostAppsSecrets: apps.PostAppsSecrets,
68
56
  PostAppsSecretsDelete: apps.PostAppsSecretsDelete,
69
57
  PostBillingPortalConfigurations: billing_portal.PostBillingPortalConfigurations,
70
- PostBillingPortalConfigurationsConfiguration: billing_portal.PostBillingPortalConfigurationsConfiguration,
71
58
  PostBillingPortalSessions: billing_portal.PostBillingPortalSessions,
72
59
  PostCheckoutSessions: checkout.PostCheckoutSessions,
73
- PostCheckoutSessionsSessionExpire: checkout.PostCheckoutSessionsSessionExpire,
74
60
  PostCoupons: coupons.PostCoupons,
75
61
  PostCustomers: customers.PostCustomers,
76
- PostCustomersCustomerBalanceTransactions: customers.PostCustomersCustomerBalanceTransactions,
77
- PostInvoiceitems: invoiceitems.PostInvoiceitems,
62
+ PostCustomersCustomer: customers.PostCustomersCustomer,
78
63
  PostInvoices: invoices.PostInvoices,
79
64
  PostInvoicesCreatePreview: invoices.PostInvoicesCreatePreview,
80
- PostInvoicesInvoiceFinalize: invoices.PostInvoicesInvoiceFinalize,
81
65
  PostInvoicesInvoicePay: invoices.PostInvoicesInvoicePay,
82
- PostInvoicesInvoiceSend: invoices.PostInvoicesInvoiceSend,
83
66
  PostInvoicesInvoiceVoid: invoices.PostInvoicesInvoiceVoid,
67
+ PostIssuingAuthorizationsAuthorizationApprove: issuing.PostIssuingAuthorizationsAuthorizationApprove,
68
+ PostIssuingAuthorizationsAuthorizationDecline: issuing.PostIssuingAuthorizationsAuthorizationDecline,
69
+ PostIssuingCards: issuing.PostIssuingCards,
70
+ PostIssuingCardsCard: issuing.PostIssuingCardsCard,
84
71
  PostPaymentIntents: payment_intents.PostPaymentIntents,
85
72
  PostPaymentIntentsIntentCancel: payment_intents.PostPaymentIntentsIntentCancel,
86
- PostPaymentIntentsIntentConfirm: payment_intents.PostPaymentIntentsIntentConfirm,
87
- PostPaymentMethods: payment_methods.PostPaymentMethods,
88
- PostPaymentMethodsPaymentMethodAttach: payment_methods.PostPaymentMethodsPaymentMethodAttach,
89
73
  PostPaymentMethodsPaymentMethodDetach: payment_methods.PostPaymentMethodsPaymentMethodDetach,
90
74
  PostPayouts: payouts.PostPayouts,
91
75
  PostPrices: prices.PostPrices,
@@ -93,15 +77,9 @@ export const stripeSemantics = {
93
77
  PostPromotionCodes: promotion_codes.PostPromotionCodes,
94
78
  PostRadarValueListItems: radar.PostRadarValueListItems,
95
79
  PostRefunds: refunds.PostRefunds,
96
- PostSetupIntents: setup_intents.PostSetupIntents,
97
- PostSetupIntentsIntentConfirm: setup_intents.PostSetupIntentsIntentConfirm,
98
- PostSubscriptionItems: subscription_items.PostSubscriptionItems,
99
- PostSubscriptionItemsItem: subscription_items.PostSubscriptionItemsItem,
100
- PostSubscriptionSchedules: subscription_schedules.PostSubscriptionSchedules,
101
- PostSubscriptionSchedulesScheduleRelease: subscription_schedules.PostSubscriptionSchedulesScheduleRelease,
102
- PostSubscriptions: subscriptions.PostSubscriptions,
103
80
  PostSubscriptionsSubscriptionExposedId: subscriptions.PostSubscriptionsSubscriptionExposedId,
104
- PostTestHelpersTestClocks: test_helpers.PostTestHelpersTestClocks,
105
- PostTestHelpersTestClocksTestClockAdvance: test_helpers.PostTestHelpersTestClocksTestClockAdvance,
81
+ PostTestHelpersIssuingAuthorizations: test_helpers.PostTestHelpersIssuingAuthorizations,
82
+ PostTestHelpersIssuingAuthorizationsAuthorizationCapture: test_helpers.PostTestHelpersIssuingAuthorizationsAuthorizationCapture,
106
83
  PostTransfers: transfers.PostTransfers,
84
+ PostWebhookEndpoints: webhook_endpoints.PostWebhookEndpoints,
107
85
  };
@@ -1,16 +1,11 @@
1
1
  import type { HandlerContext } from '@volter/world-core';
2
- export declare function DeleteInvoicesInvoice(ctx: HandlerContext): Promise<Response>;
3
2
  export declare function GetInvoicesInvoice(ctx: HandlerContext): Promise<Response>;
4
- export declare function GetInvoicesInvoiceLines(ctx: HandlerContext): Promise<Response>;
3
+ export declare function PostInvoicesInvoicePay(ctx: HandlerContext): Promise<Response>;
4
+ export declare function PostInvoicesInvoiceVoid(ctx: HandlerContext): Promise<Response>;
5
5
  export declare function PostInvoices(ctx: HandlerContext): Promise<Response>;
6
6
  /**
7
7
  * A preview of the next invoice for a customer or subscription (create_preview), computed and never
8
8
  * stored: the subscription's items at their prices, the customer's pending items, the subscription's
9
- * discount, and with `subscription_details.proration_date` a credit for the unused period against a
10
- * charge for the new one (split evenly: there is no clock here to measure the remaining fraction).
9
+ * discount, and an update's unused-time credit and remaining-time charge at the old and new prices.
11
10
  */
12
11
  export declare function PostInvoicesCreatePreview(ctx: HandlerContext): Promise<Response>;
13
- export declare function PostInvoicesInvoiceFinalize(ctx: HandlerContext): Promise<Response>;
14
- export declare function PostInvoicesInvoicePay(ctx: HandlerContext): Promise<Response>;
15
- export declare function PostInvoicesInvoiceSend(ctx: HandlerContext): Promise<Response>;
16
- export declare function PostInvoicesInvoiceVoid(ctx: HandlerContext): Promise<Response>;