@crvouga/mockingbird-service-flex 0.1.0 → 0.3.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 +12 -0
- package/README.md +28 -9
- package/dist/{chunk-Q6AKODYZ.js → chunk-7ZR3HG2Y.js} +2 -2
- package/dist/{chunk-RB3RIE5G.js → chunk-CTPIFEBJ.js} +114 -10
- package/dist/chunk-CTPIFEBJ.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +61 -8
- package/dist/index.js +5 -1
- package/dist/server.d.ts +48 -1
- package/dist/server.js +2 -2
- package/package.json +4 -4
- package/dist/chunk-RB3RIE5G.js.map +0 -7
- /package/dist/{chunk-Q6AKODYZ.js.map → chunk-7ZR3HG2Y.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-flex
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-09-24)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- add subscription-mode hosted checkout and promote the service to ready ([dd42cc2](https://github.com/crvouga/mockingbird/commit/dd42cc247a83588cb2b84b89eed4f870494b084f))
|
|
8
|
+
|
|
9
|
+
## 0.2.0 (2026-09-23)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- replace self-hosted Vault and Turborepo cache with GitHub ([963ecd8](https://github.com/crvouga/mockingbird/commit/963ecd862e50f2782acf16aaef0d25c518f87afc))
|
|
14
|
+
|
|
3
15
|
## 0.1.0 (2026-09-22)
|
|
4
16
|
|
|
5
17
|
Initial release.
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @crvouga/mockingbird-service-flex
|
|
2
2
|
|
|
3
3
|
Stateful mock of the **Flex** (withflex.com) HSA/FSA payments API for test suites: products
|
|
4
|
-
(answered from a recorded catalog corpus), checkout sessions in `payment
|
|
5
|
-
`setup` modes, customers, setup intents,
|
|
6
|
-
Svix-signed webhooks Flex posts back. A UI checkout that drove the real
|
|
4
|
+
(answered from a recorded catalog corpus), checkout sessions in `payment` (one-time),
|
|
5
|
+
`subscription`, `off_session` and `setup` modes, subscriptions, customers, setup intents,
|
|
6
|
+
refunds, the **hosted checkout page**, and the Svix-signed webhooks Flex posts back. A UI checkout that drove the real
|
|
7
7
|
`checkout.withflex.com` page and then waited on a 5-minute reconciler settles here in
|
|
8
8
|
milliseconds: the page is local, and the signed webhook reaches the app as soon as the card is
|
|
9
9
|
accepted.
|
|
@@ -11,7 +11,9 @@ accepted.
|
|
|
11
11
|
- Operation coverage: [SUPPORT.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/flex/SUPPORT.md)
|
|
12
12
|
- Flex publishes no machine-readable spec: the contract (`openapi.yaml`) is hand-authored from
|
|
13
13
|
the wire shapes our consumer reads and writes (`B/billing/flex/`), and every field its zod
|
|
14
|
-
schemas require is served.
|
|
14
|
+
schemas require is served. Subscription mode, `price_data.recurring` and the subscription
|
|
15
|
+
object follow the [Flex API reference](https://docs.withflex.com/api-reference) (there is
|
|
16
|
+
no sandbox recording).
|
|
15
17
|
|
|
16
18
|
## Install
|
|
17
19
|
|
|
@@ -73,6 +75,16 @@ const { checkout_session } = (await (
|
|
|
73
75
|
await post(`/__admin/sessions/${checkout_session.checkout_session_id}/complete`, {})
|
|
74
76
|
```
|
|
75
77
|
|
|
78
|
+
### Browser E2E suites
|
|
79
|
+
|
|
80
|
+
Run `npx mockingbird-flex serve` on a port, point `FLEX_API_BASE_URL` at it, and the browser
|
|
81
|
+
lands on the mock's page instead of `checkout.withflex.com`: no network, no shared Flex
|
|
82
|
+
account, and the signed webhook reaches the app as soon as Pay is clicked. The page URL takes
|
|
83
|
+
the mock's origin, or `--public-url` when given. A suite that finds the Flex tab by host
|
|
84
|
+
(`/\bcheckout\.withflex\.com\b/`) keeps working with
|
|
85
|
+
`--public-url http://checkout.withflex.com.localhost:8792`: Chromium resolves every
|
|
86
|
+
`*.localhost` name to loopback. Other browsers may need a hosts entry.
|
|
87
|
+
|
|
76
88
|
### Routes
|
|
77
89
|
|
|
78
90
|
All API bodies are wrapped: `{product: {…}}`, `{checkout_session: {…}}`, `{customer: {…}}`,
|
|
@@ -85,12 +97,13 @@ Errors are `{error: {type, message, param?}}`.
|
|
|
85
97
|
| `POST /v1/products` | `{product: {name, description?, url?, client_reference_id?, metadata?}}`. New products are active, `hsa_fsa_eligibility: null` until classified (`PUT /__admin/products/:id`). |
|
|
86
98
|
| `GET /v1/products/{id}` | `product_id, name, description, url, client_reference_id, hsa_fsa_eligibility, visit_type, active, test_mode, metadata, created_at`. |
|
|
87
99
|
| `PATCH /v1/products/{id}` | `{product: {active?, name?, description?, url?, metadata?}}`; emits `product.updated`. |
|
|
88
|
-
| `POST /v1/checkout/sessions` | `Idempotency-Key` honoured. `mode` `payment` (≥1 line item), `setup` (a customer and no line items, else 400), `off_session` (customer + a saved `payment_method`, charged before answering: the response is already `complete`, or its expanded payment intent is `requires_payment_method` / `requires_action` per `offSessionOutcome`). Unknown or inactive products, customers and payment methods are 400. `redirect_url` and `url` are the hosted page. |
|
|
100
|
+
| `POST /v1/checkout/sessions` | `Idempotency-Key` honoured. `mode` `payment` (≥1 line item), `subscription` (≥1 line item whose `price_data.recurring` is `{interval: day\|week\|month\|year, interval_count?}`, else 400; optional `subscription_data: {cancel_at_period_end?, metadata?}`), `setup` (a customer and no line items, else 400), `off_session` (customer + a saved `payment_method`, charged before answering: the response is already `complete`, or its expanded payment intent is `requires_payment_method` / `requires_action` per `offSessionOutcome`). Unknown or inactive products, customers and payment methods are 400. `redirect_url` and `url` are the hosted page. |
|
|
89
101
|
| `GET /v1/checkout/sessions/{id}?expand_customer=true&expand_payment_intent=true` | The session; expansions return `customer` / `payment_intent` objects instead of ids. |
|
|
90
102
|
| `GET /v1/checkout/sessions?client_reference_id=&limit=&starting_after=` | Newest first (our ambiguous-create recovery). |
|
|
91
103
|
| `POST /v1/checkout/sessions/{id}/refund` | `Idempotency-Key` honoured. `{checkout_session: {}}` (full) or `{checkout_session: {amount}}`; 400 when unpaid or over-refunded. |
|
|
92
104
|
| `POST /v1/customers` | `Idempotency-Key` honoured. `{customer: {first_name, last_name, email, phone}}` (all required). |
|
|
93
105
|
| `GET /v1/setup_intents/{id}?expand=customer,payment_method` | `setup_intent_id, status, customer, payment_method`. |
|
|
106
|
+
| `GET /v1/subscriptions/{id}` | `{subscription: {subscription_id, status, items, customer, default_payment_method, cancel_at_period_end, current_period_start, current_period_end, canceled_at, metadata, test_mode, created_at}}`. A paid subscription-mode session starts one: `active`, `items` = its recurring line items, the period one interval of the first recurring item (month/year steps clamp to the month's last day), charged to the card just used; the session's `subscription` holds its id. |
|
|
94
107
|
|
|
95
108
|
Auth: `Authorization: Bearer fsk_test_…` or `fsk_…`; a missing key, or any other format
|
|
96
109
|
(`sk_test_…`, `whsec_…`), is 401 `authentication_error`. `test_mode` on created objects follows
|
|
@@ -129,6 +142,7 @@ intent plus `checkout_session_id`, refund events `checkout_session` and `payment
|
|
|
129
142
|
| Event | When |
|
|
130
143
|
| --- | --- |
|
|
131
144
|
| `payment_intent.succeeded`, then `checkout.session.completed` | the page (or `…/complete`, or an off-session charge) settles a session |
|
|
145
|
+
| `customer.subscription.created` (the subscription), before those two | a subscription-mode session settles |
|
|
132
146
|
| `checkout.session.async_payment_succeeded` | settling a session whose intent was `processing` |
|
|
133
147
|
| `checkout.session.async_payment_failed` | a decline (page, admin, off-session) |
|
|
134
148
|
| `checkout.session.expired` | `…/expire`, or `expires_at` passing on the mock clock (default 24 h) |
|
|
@@ -188,8 +202,12 @@ so product names are synthesised.
|
|
|
188
202
|
split payment is reachable only as `next_action: provide_second_payment_method`.
|
|
189
203
|
- The letter-of-medical-necessity questionnaire: one submit button stands in for it.
|
|
190
204
|
- Test/live data separation: a live key sees the same objects (only `test_mode` differs).
|
|
191
|
-
-
|
|
192
|
-
|
|
205
|
+
- Subscription lifecycle after checkout: renewals, invoices and the `invoice.*` events,
|
|
206
|
+
trials, `customer.subscription.updated` / `.deleted`, and the subscription update/cancel
|
|
207
|
+
routes. A subscription stays `active` for its first period. The Prices API (`price` ids in
|
|
208
|
+
line items) is not modelled either: use inline `price_data`.
|
|
209
|
+
- Coupons, promotion codes (`allow_promotion_codes` is echoed only), partial captures,
|
|
210
|
+
disputes.
|
|
193
211
|
- Flex's exact error texts and ids: shapes follow what our consumer reads; ids look like
|
|
194
212
|
`fprod_01z…`, `fcs_01z…`, `fcus_…`, `fpi_…`, `fseti_…`, `fpm_…`, `fevt_…`.
|
|
195
213
|
|
|
@@ -197,7 +215,7 @@ so product names are synthesised.
|
|
|
197
215
|
|
|
198
216
|
| Export | Kind | Description |
|
|
199
217
|
| --- | --- | --- |
|
|
200
|
-
| `FlexAPI` | class | The in-process mock: `fetch(request)`, `reset()`, `settle(id)`, `decline(id)`, `expire(id)`, `requireAction(id, type)`, `setPaymentIntent(id, patch)`, `applyRefund(id, amount)`, `putProduct(product)`, `emitFor(type, target)`, `tick()`, `sessions()`, `present(session, view)`. Options: `sqlite`, `now`, `namespace`, `publicNamespace`, `products`, `settings`, `onEvent`. |
|
|
218
|
+
| `FlexAPI` | class | The in-process mock: `fetch(request)`, `reset()`, `settle(id)`, `decline(id)`, `expire(id)`, `requireAction(id, type)`, `setPaymentIntent(id, patch)`, `applyRefund(id, amount)`, `putProduct(product)`, `emitFor(type, target)`, `tick()`, `sessions()`, `subscriptions()`, `present(session, view)`. Options: `sqlite`, `now`, `namespace`, `publicNamespace`, `products`, `settings`, `onEvent`. |
|
|
201
219
|
| `createRuntime` | function | The mock with the full service contract. Options: `webhooks: {url, secret, retryDelaysMs?, fetch?}`, `products`, `settings`, `tickMs`, `clock`, `seed`, `adminKey`, `onLog`. |
|
|
202
220
|
| `FLEX_PRESETS` | object | Every named fault preset. |
|
|
203
221
|
| `FLEX_NAMESPACE` | string | The service name, `"flex"`. |
|
|
@@ -206,7 +224,8 @@ so product names are synthesised.
|
|
|
206
224
|
| `isNextActionType` | function | Whether a string is a next-action type. |
|
|
207
225
|
| `CARDS`, `classifyCard`, `substituteSessionId` | values | The hosted page's test cards, its card classifier, and the `{CHECKOUT_SESSION_ID}` substitution. |
|
|
208
226
|
| `CORPUS_ROWS`, `corpusProduct` | values | The recorded product corpus and its row → product mapping. |
|
|
209
|
-
| `DEFAULT_SETTINGS`, `ELIGIBILITIES`, `NEXT_ACTION_TYPES`, `PAYMENT_INTENT_STATUSES` | values | Defaults and enums. |
|
|
227
|
+
| `DEFAULT_SETTINGS`, `ELIGIBILITIES`, `NEXT_ACTION_TYPES`, `PAYMENT_INTENT_STATUSES`, `SUBSCRIPTION_STATUSES` | values | Defaults and enums. |
|
|
228
|
+
| `periodEnd` | function | `periodEnd(startMs, {interval, interval_count?})`: the end of a billing period (epoch ms). |
|
|
210
229
|
| `document`, `operationIds`, `supportedOperationIds` | values | The vendored OpenAPI contract and its operation ids. |
|
|
211
230
|
| `createServer`, `serveTarget`, `DEFAULT_PORT` (`./server`) | Node | Serve over `node:http` (expiry ticks every 100 ms); the `serve` CLI target (`--webhook-url`, `--webhook-secret`, `--public-url`, `--event-naming`); port 8792. |
|
|
212
231
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CTPIFEBJ.js";
|
|
4
4
|
|
|
5
5
|
// ../../adapters/node/dist/cli.js
|
|
6
6
|
import { readFile } from "node:fs/promises";
|
|
@@ -377,4 +377,4 @@ export {
|
|
|
377
377
|
createServer2 as createServer,
|
|
378
378
|
serveTarget
|
|
379
379
|
};
|
|
380
|
-
//# sourceMappingURL=chunk-
|
|
380
|
+
//# sourceMappingURL=chunk-7ZR3HG2Y.js.map
|
|
@@ -1550,7 +1550,7 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
|
1550
1550
|
};
|
|
1551
1551
|
|
|
1552
1552
|
// ../core/dist/version.js
|
|
1553
|
-
var PACKAGE_VERSION = true ? "0.
|
|
1553
|
+
var PACKAGE_VERSION = true ? "0.3.0" : UNRELEASED_VERSION;
|
|
1554
1554
|
|
|
1555
1555
|
// ../core/dist/signing.js
|
|
1556
1556
|
var encoder = new TextEncoder();
|
|
@@ -2550,9 +2550,9 @@ var bodyIssues = (context, contentType = "application/json") => {
|
|
|
2550
2550
|
};
|
|
2551
2551
|
|
|
2552
2552
|
// src/generated/openapi.ts
|
|
2553
|
-
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Flex HSA/FSA payments API (Mockingbird subset)","description":"Stateful mock subset of the Flex (withflex.com) payments API: products, checkout sessions\\n(payment, off-session and setup modes), customers, setup intents, refunds, and the hosted\\ncheckout page. Request and response bodies are wrapped in an envelope named after the\\nobject (\`{product: {...}}\`, \`{checkout_session: {...}}\`, \`{products: [...]}\`).\\nHand-authored from the consumer's wire shapes (Flex publishes no machine-readable spec).\\n","version":"1","x-mockingbird-upstream":{"note":"Hand-authored from the wire shapes our consumer reads and writes (flex-api.client.ts, flex-payment.types.ts, flex-product.types.ts, flex-webhook.service.ts, do-flex-hosted-checkout.ts)."}},"servers":[{"url":"https://api.withflex.com"}],"security":[{"bearerAuth":[]}],"paths":{"/v1/products":{"get":{"operationId":"ListProducts","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"starting_after","in":"query","required":false,"description":"The last product_id of the previous page.","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"product","missing":"fprod_00000000000000000000000000"}}}],"responses":{"200":{"description":"A page of products, oldest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"CreateProduct","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductBody"}}}},"responses":{"200":{"description":"The created product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/products/{productId}":{"parameters":[{"name":"productId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"product","missing":"fprod_00000000000000000000000000"}}}],"get":{"operationId":"GetProduct","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"UpdateProduct","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductBody"}}}},"responses":{"200":{"description":"The updated product (emits product.updated)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/checkout/sessions":{"get":{"operationId":"ListCheckoutSessions","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"client_reference_id","in":"query","required":false,"schema":{"type":"string","maxLength":255}},{"name":"expand_customer","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"expand_payment_intent","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"starting_after","in":"query","required":false,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"checkout_session","missing":"fcs_00000000000000000000000000"}}}],"responses":{"200":{"description":"Sessions, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"CreateCheckoutSession","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9:_.-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutSessionBody"}}}},"responses":{"200":{"description":"The created session. Off-session mode charges the saved payment method before answering, so the session is already complete (or its payment intent failed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/v1/checkout/sessions/{sessionId}":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"checkout_session","missing":"fcs_00000000000000000000000000"}}}],"get":{"operationId":"GetCheckoutSession","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"expand_customer","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"expand_payment_intent","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"The session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/checkout/sessions/{sessionId}/refund":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"checkout_session","missing":"fcs_00000000000000000000000000"}}}],"post":{"operationId":"RefundCheckoutSession","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9:_.-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCheckoutSessionBody"}}}},"responses":{"200":{"description":"The session with amount_refunded updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}}},"/v1/customers":{"post":{"operationId":"CreateCustomer","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9:_.-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerBody"}}}},"responses":{"200":{"description":"The created customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}}},"/v1/setup_intents/{setupIntentId}":{"parameters":[{"name":"setupIntentId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"setup_intent","missing":"fseti_00000000000000000000000000"}}}],"get":{"operationId":"GetSetupIntent","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"expand","in":"query","required":false,"description":"Comma-separated fields to expand (customer, payment_method).","schema":{"type":"string","enum":["customer","payment_method","customer,payment_method"]}}],"responses":{"200":{"description":"The setup intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupIntentEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/pay/{sessionId}":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"operationId":"HostedCheckoutPage","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"A browser-facing HTML page; covered by the acceptance suite, not by JSON walks."}},"responses":{"200":{"description":"The card form (or the letter-of-medical-necessity step)","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Unknown session","content":{"text/html":{"schema":{"type":"string"}}}},"409":{"description":"The session is no longer payable","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"operationId":"SubmitHostedCheckout","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":false,"reason":"A browser form post; covered by the acceptance suite."}},"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"step":{"type":"string","enum":["card","lmn"]},"cardNumber":{"type":"string"},"expiry":{"type":"string"},"cvc":{"type":"string"},"postalCode":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phone":{"type":"string"}}}}}},"responses":{"302":{"description":"Paid; redirects to success_url (or to the next action's page)"},"400":{"description":"The card form is incomplete or the card number is invalid","content":{"text/html":{"schema":{"type":"string"}}}},"402":{"description":"The card was declined","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Unknown session","content":{"text/html":{"schema":{"type":"string"}}}},"409":{"description":"The session is no longer payable","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/pay/{sessionId}/cancel":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"operationId":"CancelHostedCheckout","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"A browser navigation to cancel_url; covered by the acceptance suite."}},"responses":{"302":{"description":"Redirects to cancel_url; the session stays open"},"404":{"description":"Unknown session","content":{"text/html":{"schema":{"type":"string"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A secret key, \`fsk_test_\u2026\` (test mode) or \`fsk_\u2026\` (live mode)."}},"responses":{"Error":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"schemas":{"ErrorBody":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","idempotency_error","api_error","card_error"]},"code":{"type":"string"},"message":{"type":"string"},"param":{"type":"string"}}}}},"StringMap":{"type":"object","maxProperties":50,"additionalProperties":{"type":"string","maxLength":500}},"Product":{"type":"object","required":["product_id","name","active","test_mode","created_at"],"properties":{"product_id":{"type":"string","x-mockingbird-resource":{"type":"product","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"description":{"type":["string","null"]},"url":{"type":["string","null"]},"client_reference_id":{"type":["string","null"]},"hsa_fsa_eligibility":{"description":"not_eligible, auto_substantiation, private_label, letter_of_medical_necessity, prescription, vision or service; null until Flex classifies a new product.","type":["string","null"]},"visit_type":{"type":["string","null"]},"active":{"type":"boolean"},"test_mode":{"type":"boolean"},"metadata":{"type":["object","null"],"additionalProperties":{"type":"string"}},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ProductEnvelope":{"type":"object","required":["product"],"properties":{"product":{"$ref":"#/components/schemas/Product"}}},"ProductList":{"type":"object","required":["products","has_more"],"properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"has_more":{"type":"boolean"}}},"CreateProductBody":{"type":"object","required":["product"],"properties":{"product":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":250},"description":{"type":"string","maxLength":1000},"url":{"type":"string","maxLength":2048},"client_reference_id":{"type":"string","minLength":1,"maxLength":255},"metadata":{"$ref":"#/components/schemas/StringMap"}}}}},"UpdateProductBody":{"type":"object","required":["product"],"properties":{"product":{"type":"object","properties":{"active":{"type":"boolean"},"name":{"type":"string","minLength":1,"maxLength":250},"description":{"type":"string","maxLength":1000},"url":{"type":"string","maxLength":2048},"metadata":{"$ref":"#/components/schemas/StringMap"}}}}},"LineItem":{"type":"object","required":["price_data","quantity"],"properties":{"price_data":{"type":"object","required":["product","unit_amount"],"properties":{"product":{"type":"string","x-mockingbird-resource-ref":{"type":"product","missing":"fprod_00000000000000000000000000"}},"unit_amount":{"type":"integer","minimum":0,"maximum":10000000}}},"quantity":{"type":"integer","minimum":1,"maximum":100}}},"CreateCheckoutSessionBody":{"type":"object","required":["checkout_session"],"properties":{"checkout_session":{"type":"object","required":["success_url","line_items"],"properties":{"allow_promotion_codes":{"type":"boolean"},"capture_method":{"type":"string","enum":["automatic"]},"cancel_url":{"type":"string","minLength":1,"maxLength":2048},"success_url":{"type":"string","minLength":1,"maxLength":2048},"client_reference_id":{"type":"string","minLength":1,"maxLength":255},"line_items":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/LineItem"}},"metadata":{"$ref":"#/components/schemas/StringMap"},"mode":{"type":"string","enum":["payment","off_session","setup"]},"payment_method":{"type":"string","maxLength":255},"customer":{"type":"string","x-mockingbird-resource-ref":{"type":"customer","missing":"fcus_00000000000000000000000000"}},"setup_future_use":{"type":"string","enum":["off_session"]}}}}},"RefundCheckoutSessionBody":{"type":"object","required":["checkout_session"],"properties":{"checkout_session":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"maximum":10000000}}}}},"CreateCustomerBody":{"type":"object","required":["customer"],"properties":{"customer":{"type":"object","required":["first_name","last_name","email","phone"],"properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","minLength":1,"maxLength":100},"email":{"type":"string","format":"email","maxLength":254},"phone":{"type":"string","minLength":1,"maxLength":32}}}}},"Customer":{"type":"object","required":["customer_id","test_mode","created_at"],"properties":{"customer_id":{"type":"string","x-mockingbird-resource":{"type":"customer","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"test_mode":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"CustomerEnvelope":{"type":"object","required":["customer"],"properties":{"customer":{"$ref":"#/components/schemas/Customer"}}},"PaymentMethod":{"type":"object","required":["payment_method_id","type"],"properties":{"payment_method_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"type":{"type":"string","enum":["card"]},"hsa_fsa":{"type":"boolean"},"customer":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}}}},"PaymentIntent":{"type":"object","required":["payment_intent_id","amount","status"],"properties":{"payment_intent_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"amount":{"type":"integer"},"amount_received":{"type":["integer","null"]},"customer":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"payment_method":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"status":{"type":"string","enum":["requires_payment_method","requires_action","processing","succeeded","canceled"]},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"SetupIntent":{"type":"object","required":["setup_intent_id","status"],"properties":{"setup_intent_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"status":{"type":"string","enum":["requires_payment_method","requires_action","processing","succeeded","canceled"]},"customer":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/Customer"},{"type":"null"}]},"payment_method":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/PaymentMethod"},{"type":"null"}]},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"SetupIntentEnvelope":{"type":"object","required":["setup_intent"],"properties":{"setup_intent":{"$ref":"#/components/schemas/SetupIntent"}}},"NextAction":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["collect_letter_of_medical_necessity","provide_second_payment_method","provide_alternative_payment_method","payment_failed"]},"collect_letter_of_medical_necessity":{"type":"object","required":["url"],"properties":{"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}}}},"provide_second_payment_method":{"type":"object","required":["url"],"properties":{"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}}}},"provide_alternative_payment_method":{"type":"object","required":["url"],"properties":{"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}}}},"payment_failed":{"type":"object"}}},"ResponseLineItem":{"type":"object","required":["price_data","quantity","amount_total"],"properties":{"price_data":{"type":"object","required":["product","unit_amount"],"properties":{"product":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"unit_amount":{"type":"integer"}}},"quantity":{"type":"integer"},"amount_total":{"type":"integer"}}},"CheckoutSession":{"type":"object","required":["checkout_session_id","amount_total","status","mode","redirect_url","url","test_mode","created_at"],"properties":{"checkout_session_id":{"type":"string","x-mockingbird-resource":{"type":"checkout_session","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"client_reference_id":{"type":["string","null"]},"amount_total":{"type":"integer","minimum":0},"amount_received":{"type":["integer","null"]},"amount_refunded":{"type":"integer","minimum":0},"customer":{"anyOf":[{"type":"string","x-mockingbird-volatile":{"kind":"id"}},{"$ref":"#/components/schemas/Customer"},{"type":"null"}]},"payment_intent":{"anyOf":[{"type":"string","x-mockingbird-volatile":{"kind":"id"}},{"$ref":"#/components/schemas/PaymentIntent"},{"type":"null"}]},"setup_intent":{"type":["string","null"],"x-mockingbird-resource":{"type":"setup_intent","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"mode":{"type":"string","enum":["payment","off_session","setup"]},"status":{"type":"string","enum":["open","paid","complete","canceled","expired"]},"redirect_url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"success_url":{"type":"string"},"cancel_url":{"type":["string","null"]},"next_action":{"anyOf":[{"$ref":"#/components/schemas/NextAction"},{"type":"null"}]},"visit_type":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":{"type":"string"}},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/ResponseLineItem"}},"allow_promotion_codes":{"type":"boolean"},"capture_method":{"type":"string"},"setup_future_use":{"type":["string","null"]},"test_mode":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"expires_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"CheckoutSessionEnvelope":{"type":"object","required":["checkout_session"],"properties":{"checkout_session":{"$ref":"#/components/schemas/CheckoutSession"}}},"CheckoutSessionList":{"type":"object","required":["checkout_sessions","has_more"],"properties":{"checkout_sessions":{"type":"array","items":{"$ref":"#/components/schemas/CheckoutSession"}},"has_more":{"type":"boolean"}}}}}}`);
|
|
2554
|
-
var operationIds = ["ListProducts", "CreateProduct", "GetProduct", "UpdateProduct", "ListCheckoutSessions", "CreateCheckoutSession", "GetCheckoutSession", "RefundCheckoutSession", "CreateCustomer", "GetSetupIntent", "HostedCheckoutPage", "SubmitHostedCheckout", "CancelHostedCheckout"];
|
|
2555
|
-
var supportedOperationIds = ["ListProducts", "CreateProduct", "GetProduct", "UpdateProduct", "ListCheckoutSessions", "CreateCheckoutSession", "GetCheckoutSession", "RefundCheckoutSession", "CreateCustomer", "GetSetupIntent", "HostedCheckoutPage", "SubmitHostedCheckout", "CancelHostedCheckout"];
|
|
2553
|
+
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Flex HSA/FSA payments API (Mockingbird subset)","description":"Stateful mock subset of the Flex (withflex.com) payments API: products, checkout sessions\\n(payment, subscription, off-session and setup modes), subscriptions, customers, setup\\nintents, refunds, and the hosted checkout page. Request and response bodies are wrapped in an envelope named after the\\nobject (\`{product: {...}}\`, \`{checkout_session: {...}}\`, \`{products: [...]}\`).\\nHand-authored from the consumer's wire shapes and the Flex API reference\\n(https://docs.withflex.com/api-reference); Flex publishes no machine-readable spec.\\n","version":"1","x-mockingbird-upstream":{"note":"Hand-authored from the wire shapes our consumer reads and writes (flex-api.client.ts, flex-payment.types.ts, flex-product.types.ts, flex-webhook.service.ts, do-flex-hosted-checkout.ts). Subscription mode, recurring price_data and the Subscription object follow https://docs.withflex.com/api-reference/checkout-sessions/create-checkout-session, https://docs.withflex.com/api-reference/subscriptions/get-subscription and https://docs.withflex.com/api-reference/prices/create-price."}},"servers":[{"url":"https://api.withflex.com"}],"security":[{"bearerAuth":[]}],"paths":{"/v1/products":{"get":{"operationId":"ListProducts","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"starting_after","in":"query","required":false,"description":"The last product_id of the previous page.","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"product","missing":"fprod_00000000000000000000000000"}}}],"responses":{"200":{"description":"A page of products, oldest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"CreateProduct","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductBody"}}}},"responses":{"200":{"description":"The created product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v1/products/{productId}":{"parameters":[{"name":"productId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"product","missing":"fprod_00000000000000000000000000"}}}],"get":{"operationId":"GetProduct","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"UpdateProduct","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductBody"}}}},"responses":{"200":{"description":"The updated product (emits product.updated)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/checkout/sessions":{"get":{"operationId":"ListCheckoutSessions","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"client_reference_id","in":"query","required":false,"schema":{"type":"string","maxLength":255}},{"name":"expand_customer","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"expand_payment_intent","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"starting_after","in":"query","required":false,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"checkout_session","missing":"fcs_00000000000000000000000000"}}}],"responses":{"200":{"description":"Sessions, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionList"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"CreateCheckoutSession","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9:_.-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutSessionBody"}}}},"responses":{"200":{"description":"The created session. Off-session mode charges the saved payment method before answering, so the session is already complete (or its payment intent failed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/v1/checkout/sessions/{sessionId}":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"checkout_session","missing":"fcs_00000000000000000000000000"}}}],"get":{"operationId":"GetCheckoutSession","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"expand_customer","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"expand_payment_intent","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"The session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/checkout/sessions/{sessionId}/refund":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"checkout_session","missing":"fcs_00000000000000000000000000"}}}],"post":{"operationId":"RefundCheckoutSession","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9:_.-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCheckoutSessionBody"}}}},"responses":{"200":{"description":"The session with amount_refunded updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}}},"/v1/customers":{"post":{"operationId":"CreateCustomer","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9:_.-]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerBody"}}}},"responses":{"200":{"description":"The created customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerEnvelope"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}}},"/v1/setup_intents/{setupIntentId}":{"parameters":[{"name":"setupIntentId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"setup_intent","missing":"fseti_00000000000000000000000000"}}}],"get":{"operationId":"GetSetupIntent","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"expand","in":"query","required":false,"description":"Comma-separated fields to expand (customer, payment_method).","schema":{"type":"string","enum":["customer","payment_method","customer,payment_method"]}}],"responses":{"200":{"description":"The setup intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupIntentEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/subscriptions/{subscriptionId}":{"parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","x-mockingbird-resource-ref":{"type":"subscription","missing":"fsub_00000000000000000000000000"}}}],"get":{"operationId":"GetSubscription","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"The subscription a completed subscription-mode checkout session created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionEnvelope"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/pay/{sessionId}":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"operationId":"HostedCheckoutPage","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"A browser-facing HTML page; covered by the acceptance suite, not by JSON walks."}},"responses":{"200":{"description":"The card form (or the letter-of-medical-necessity step)","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Unknown session","content":{"text/html":{"schema":{"type":"string"}}}},"409":{"description":"The session is no longer payable","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"operationId":"SubmitHostedCheckout","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":false,"reason":"A browser form post; covered by the acceptance suite."}},"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"step":{"type":"string","enum":["card","lmn"]},"cardNumber":{"type":"string"},"expiry":{"type":"string"},"cvc":{"type":"string"},"postalCode":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phone":{"type":"string"}}}}}},"responses":{"302":{"description":"Paid; redirects to success_url (or to the next action's page)"},"400":{"description":"The card form is incomplete or the card number is invalid","content":{"text/html":{"schema":{"type":"string"}}}},"402":{"description":"The card was declined","content":{"text/html":{"schema":{"type":"string"}}}},"404":{"description":"Unknown session","content":{"text/html":{"schema":{"type":"string"}}}},"409":{"description":"The session is no longer payable","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/pay/{sessionId}/cancel":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string"}}],"get":{"operationId":"CancelHostedCheckout","security":[],"x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"A browser navigation to cancel_url; covered by the acceptance suite."}},"responses":{"302":{"description":"Redirects to cancel_url; the session stays open"},"404":{"description":"Unknown session","content":{"text/html":{"schema":{"type":"string"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A secret key, \`fsk_test_\u2026\` (test mode) or \`fsk_\u2026\` (live mode)."}},"responses":{"Error":{"description":"An error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"schemas":{"ErrorBody":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","message"],"properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","idempotency_error","api_error","card_error"]},"code":{"type":"string"},"message":{"type":"string"},"param":{"type":"string"}}}}},"StringMap":{"type":"object","maxProperties":50,"additionalProperties":{"type":"string","maxLength":500}},"Product":{"type":"object","required":["product_id","name","active","test_mode","created_at"],"properties":{"product_id":{"type":"string","x-mockingbird-resource":{"type":"product","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"description":{"type":["string","null"]},"url":{"type":["string","null"]},"client_reference_id":{"type":["string","null"]},"hsa_fsa_eligibility":{"description":"not_eligible, auto_substantiation, private_label, letter_of_medical_necessity, prescription, vision or service; null until Flex classifies a new product.","type":["string","null"]},"visit_type":{"type":["string","null"]},"active":{"type":"boolean"},"test_mode":{"type":"boolean"},"metadata":{"type":["object","null"],"additionalProperties":{"type":"string"}},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ProductEnvelope":{"type":"object","required":["product"],"properties":{"product":{"$ref":"#/components/schemas/Product"}}},"ProductList":{"type":"object","required":["products","has_more"],"properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"has_more":{"type":"boolean"}}},"CreateProductBody":{"type":"object","required":["product"],"properties":{"product":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":250},"description":{"type":"string","maxLength":1000},"url":{"type":"string","maxLength":2048},"client_reference_id":{"type":"string","minLength":1,"maxLength":255},"metadata":{"$ref":"#/components/schemas/StringMap"}}}}},"UpdateProductBody":{"type":"object","required":["product"],"properties":{"product":{"type":"object","properties":{"active":{"type":"boolean"},"name":{"type":"string","minLength":1,"maxLength":250},"description":{"type":"string","maxLength":1000},"url":{"type":"string","maxLength":2048},"metadata":{"$ref":"#/components/schemas/StringMap"}}}}},"LineItem":{"type":"object","required":["price_data","quantity"],"properties":{"price_data":{"type":"object","required":["product","unit_amount"],"properties":{"product":{"type":"string","x-mockingbird-resource-ref":{"type":"product","missing":"fprod_00000000000000000000000000"}},"unit_amount":{"type":"integer","minimum":0,"maximum":10000000},"recurring":{"$ref":"#/components/schemas/Recurring"}}},"quantity":{"type":"integer","minimum":1,"maximum":100}}},"Recurring":{"type":"object","description":"Makes the line item's price recurring; a subscription-mode session needs at least one.","required":["interval"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"]},"interval_count":{"type":"integer","minimum":1,"maximum":12}}},"SubscriptionData":{"type":"object","properties":{"cancel_at_period_end":{"type":"boolean"},"metadata":{"$ref":"#/components/schemas/StringMap"}}},"CreateCheckoutSessionBody":{"type":"object","required":["checkout_session"],"properties":{"checkout_session":{"type":"object","required":["success_url","line_items"],"properties":{"allow_promotion_codes":{"type":"boolean"},"capture_method":{"type":"string","enum":["automatic"]},"cancel_url":{"type":"string","minLength":1,"maxLength":2048},"success_url":{"type":"string","minLength":1,"maxLength":2048},"client_reference_id":{"type":"string","minLength":1,"maxLength":255},"line_items":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/LineItem"}},"metadata":{"$ref":"#/components/schemas/StringMap"},"mode":{"type":"string","enum":["payment","subscription","off_session","setup"]},"subscription_data":{"$ref":"#/components/schemas/SubscriptionData"},"payment_method":{"type":"string","maxLength":255},"customer":{"type":"string","x-mockingbird-resource-ref":{"type":"customer","missing":"fcus_00000000000000000000000000"}},"setup_future_use":{"type":"string","enum":["off_session"]}}}}},"RefundCheckoutSessionBody":{"type":"object","required":["checkout_session"],"properties":{"checkout_session":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"maximum":10000000}}}}},"CreateCustomerBody":{"type":"object","required":["customer"],"properties":{"customer":{"type":"object","required":["first_name","last_name","email","phone"],"properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","minLength":1,"maxLength":100},"email":{"type":"string","format":"email","maxLength":254},"phone":{"type":"string","minLength":1,"maxLength":32}}}}},"Customer":{"type":"object","required":["customer_id","test_mode","created_at"],"properties":{"customer_id":{"type":"string","x-mockingbird-resource":{"type":"customer","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"test_mode":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"CustomerEnvelope":{"type":"object","required":["customer"],"properties":{"customer":{"$ref":"#/components/schemas/Customer"}}},"PaymentMethod":{"type":"object","required":["payment_method_id","type"],"properties":{"payment_method_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"type":{"type":"string","enum":["card"]},"hsa_fsa":{"type":"boolean"},"customer":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}}}},"PaymentIntent":{"type":"object","required":["payment_intent_id","amount","status"],"properties":{"payment_intent_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"amount":{"type":"integer"},"amount_received":{"type":["integer","null"]},"customer":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"payment_method":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"status":{"type":"string","enum":["requires_payment_method","requires_action","processing","succeeded","canceled"]},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"SetupIntent":{"type":"object","required":["setup_intent_id","status"],"properties":{"setup_intent_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"status":{"type":"string","enum":["requires_payment_method","requires_action","processing","succeeded","canceled"]},"customer":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/Customer"},{"type":"null"}]},"payment_method":{"anyOf":[{"type":"string"},{"$ref":"#/components/schemas/PaymentMethod"},{"type":"null"}]},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"SetupIntentEnvelope":{"type":"object","required":["setup_intent"],"properties":{"setup_intent":{"$ref":"#/components/schemas/SetupIntent"}}},"NextAction":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["collect_letter_of_medical_necessity","provide_second_payment_method","provide_alternative_payment_method","payment_failed"]},"collect_letter_of_medical_necessity":{"type":"object","required":["url"],"properties":{"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}}}},"provide_second_payment_method":{"type":"object","required":["url"],"properties":{"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}}}},"provide_alternative_payment_method":{"type":"object","required":["url"],"properties":{"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}}}},"payment_failed":{"type":"object"}}},"ResponseLineItem":{"type":"object","required":["price_data","quantity","amount_total"],"properties":{"price_data":{"type":"object","required":["product","unit_amount"],"properties":{"product":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"unit_amount":{"type":"integer"},"recurring":{"anyOf":[{"$ref":"#/components/schemas/Recurring"},{"type":"null"}]}}},"quantity":{"type":"integer"},"amount_total":{"type":"integer"}}},"CheckoutSession":{"type":"object","required":["checkout_session_id","amount_total","status","mode","redirect_url","url","test_mode","created_at"],"properties":{"checkout_session_id":{"type":"string","x-mockingbird-resource":{"type":"checkout_session","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"client_reference_id":{"type":["string","null"]},"amount_total":{"type":"integer","minimum":0},"amount_received":{"type":["integer","null"]},"amount_refunded":{"type":"integer","minimum":0},"customer":{"anyOf":[{"type":"string","x-mockingbird-volatile":{"kind":"id"}},{"$ref":"#/components/schemas/Customer"},{"type":"null"}]},"payment_intent":{"anyOf":[{"type":"string","x-mockingbird-volatile":{"kind":"id"}},{"$ref":"#/components/schemas/PaymentIntent"},{"type":"null"}]},"setup_intent":{"type":["string","null"],"x-mockingbird-resource":{"type":"setup_intent","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"mode":{"type":"string","enum":["payment","subscription","off_session","setup"]},"subscription":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"subscription_data":{"anyOf":[{"$ref":"#/components/schemas/SubscriptionData"},{"type":"null"}]},"status":{"type":"string","enum":["open","paid","complete","canceled","expired"]},"redirect_url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"url":{"type":"string","x-mockingbird-volatile":{"kind":"url"}},"success_url":{"type":"string"},"cancel_url":{"type":["string","null"]},"next_action":{"anyOf":[{"$ref":"#/components/schemas/NextAction"},{"type":"null"}]},"visit_type":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":{"type":"string"}},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/ResponseLineItem"}},"allow_promotion_codes":{"type":"boolean"},"capture_method":{"type":"string"},"setup_future_use":{"type":["string","null"]},"test_mode":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"expires_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"CheckoutSessionEnvelope":{"type":"object","required":["checkout_session"],"properties":{"checkout_session":{"$ref":"#/components/schemas/CheckoutSession"}}},"CheckoutSessionList":{"type":"object","required":["checkout_sessions","has_more"],"properties":{"checkout_sessions":{"type":"array","items":{"$ref":"#/components/schemas/CheckoutSession"}},"has_more":{"type":"boolean"}}},"SubscriptionItem":{"type":"object","required":["price_data","quantity"],"properties":{"price_data":{"type":"object","required":["product","unit_amount","recurring"],"properties":{"product":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"unit_amount":{"type":"integer"},"recurring":{"$ref":"#/components/schemas/Recurring"}}},"quantity":{"type":"integer"}}},"Subscription":{"type":"object","required":["subscription_id","created_at","items","status","cancel_at_period_end","test_mode"],"properties":{"subscription_id":{"type":"string","x-mockingbird-resource":{"type":"subscription","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"status":{"type":"string","enum":["incomplete","incomplete_expired","trialing","active","past_due","canceled","unpaid","paused"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionItem"}},"customer":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"default_payment_method":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"cancel_at_period_end":{"type":"boolean"},"current_period_start":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"current_period_end":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"canceled_at":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":{"type":"string"}},"test_mode":{"type":"boolean"},"created_at":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}}}},"SubscriptionEnvelope":{"type":"object","required":["subscription"],"properties":{"subscription":{"$ref":"#/components/schemas/Subscription"}}}}}}`);
|
|
2554
|
+
var operationIds = ["ListProducts", "CreateProduct", "GetProduct", "UpdateProduct", "ListCheckoutSessions", "CreateCheckoutSession", "GetCheckoutSession", "RefundCheckoutSession", "CreateCustomer", "GetSetupIntent", "GetSubscription", "HostedCheckoutPage", "SubmitHostedCheckout", "CancelHostedCheckout"];
|
|
2555
|
+
var supportedOperationIds = ["ListProducts", "CreateProduct", "GetProduct", "UpdateProduct", "ListCheckoutSessions", "CreateCheckoutSession", "GetCheckoutSession", "RefundCheckoutSession", "CreateCustomer", "GetSetupIntent", "GetSubscription", "HostedCheckoutPage", "SubmitHostedCheckout", "CancelHostedCheckout"];
|
|
2556
2556
|
|
|
2557
2557
|
// src/hosted.ts
|
|
2558
2558
|
var CARDS = {
|
|
@@ -2615,12 +2615,19 @@ ${body}
|
|
|
2615
2615
|
`;
|
|
2616
2616
|
var alert = (error2) => error2 ? `<div role="alert" data-testid="flex-mock-error">${escapeHtml(error2)}</div>` : "";
|
|
2617
2617
|
var cancelLink = (session) => session.cancel_url ? `<p><a data-testid="flex-mock-cancel" href="./${encodeURIComponent(session.checkout_session_id)}/cancel">Cancel and return</a></p>` : "";
|
|
2618
|
+
var cadence = (session) => {
|
|
2619
|
+
const recurring = session.line_items.find((item) => item.price_data.recurring)?.price_data.recurring;
|
|
2620
|
+
if (!recurring) return "";
|
|
2621
|
+
const count = recurring.interval_count ?? 1;
|
|
2622
|
+
const every = count === 1 ? recurring.interval : `${count} ${recurring.interval}s`;
|
|
2623
|
+
return ` <span data-testid="flex-mock-interval">per ${escapeHtml(every)}</span>`;
|
|
2624
|
+
};
|
|
2618
2625
|
var summary = (input) => {
|
|
2619
2626
|
const { session } = input;
|
|
2620
2627
|
const items = input.productNames.map((name) => `<li data-testid="flex-mock-line-item">${escapeHtml(name)}</li>`).join("");
|
|
2621
|
-
return `<h1
|
|
2622
|
-
<p data-testid="flex-mock-session" data-session-id="${escapeHtml(session.checkout_session_id)}">
|
|
2623
|
-
${session.mode === "setup" ? "Save a card for future payments" : `Total <strong data-testid="flex-mock-amount">${dollars(session.amount_total)}</strong
|
|
2628
|
+
return `<h1>${session.mode === "subscription" ? "Subscribe with HSA/FSA" : "Pay with HSA/FSA"}</h1>
|
|
2629
|
+
<p data-testid="flex-mock-session" data-session-id="${escapeHtml(session.checkout_session_id)}" data-mode="${escapeHtml(session.mode)}">
|
|
2630
|
+
${session.mode === "setup" ? "Save a card for future payments" : `Total <strong data-testid="flex-mock-amount">${dollars(session.amount_total)}</strong>${session.mode === "subscription" ? cadence(session) : ""}`}
|
|
2624
2631
|
</p>
|
|
2625
2632
|
${items ? `<ul>${items}</ul>` : ""}`;
|
|
2626
2633
|
};
|
|
@@ -3374,6 +3381,16 @@ var PAYMENT_INTENT_STATUSES = [
|
|
|
3374
3381
|
"succeeded",
|
|
3375
3382
|
"canceled"
|
|
3376
3383
|
];
|
|
3384
|
+
var SUBSCRIPTION_STATUSES = [
|
|
3385
|
+
"incomplete",
|
|
3386
|
+
"incomplete_expired",
|
|
3387
|
+
"trialing",
|
|
3388
|
+
"active",
|
|
3389
|
+
"past_due",
|
|
3390
|
+
"canceled",
|
|
3391
|
+
"unpaid",
|
|
3392
|
+
"paused"
|
|
3393
|
+
];
|
|
3377
3394
|
var DEFAULT_SETTINGS = {
|
|
3378
3395
|
eventNaming: "dotted",
|
|
3379
3396
|
offSessionOutcome: "succeeded",
|
|
@@ -3441,6 +3458,7 @@ var FlexState = class {
|
|
|
3441
3458
|
this.paymentIntents = new Collection(sqlite, namespace, "payment_intents");
|
|
3442
3459
|
this.setupIntents = new Collection(sqlite, namespace, "setup_intents");
|
|
3443
3460
|
this.refunds = new Collection(sqlite, namespace, "refunds");
|
|
3461
|
+
this.subscriptions = new Collection(sqlite, namespace, "subscriptions");
|
|
3444
3462
|
this.settings = new Collection(sqlite, namespace, "settings");
|
|
3445
3463
|
this.ids = new IdSequence(sqlite, namespace, "flex");
|
|
3446
3464
|
}
|
|
@@ -3452,6 +3470,7 @@ var FlexState = class {
|
|
|
3452
3470
|
paymentIntents;
|
|
3453
3471
|
setupIntents;
|
|
3454
3472
|
refunds;
|
|
3473
|
+
subscriptions;
|
|
3455
3474
|
settings;
|
|
3456
3475
|
ids;
|
|
3457
3476
|
current() {
|
|
@@ -3752,6 +3771,7 @@ var FLEX_EVENT_TYPES = [
|
|
|
3752
3771
|
"checkout.session.expired",
|
|
3753
3772
|
"checkout_session.expired",
|
|
3754
3773
|
"payment_intent.succeeded",
|
|
3774
|
+
"customer.subscription.created",
|
|
3755
3775
|
"refund.created",
|
|
3756
3776
|
"refund.updated",
|
|
3757
3777
|
"charge.refunded",
|
|
@@ -3780,6 +3800,27 @@ var sleep = (ms, signal) => new Promise((resolve) => {
|
|
|
3780
3800
|
resolve();
|
|
3781
3801
|
});
|
|
3782
3802
|
});
|
|
3803
|
+
var daysInMonth = (year, month) => new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
3804
|
+
var periodEnd = (startMs, recurring) => {
|
|
3805
|
+
const count = recurring.interval_count ?? 1;
|
|
3806
|
+
const start = new Date(startMs);
|
|
3807
|
+
if (recurring.interval === "day") return startMs + count * 864e5;
|
|
3808
|
+
if (recurring.interval === "week") return startMs + count * 7 * 864e5;
|
|
3809
|
+
const months = recurring.interval === "month" ? count : count * 12;
|
|
3810
|
+
const total = start.getUTCMonth() + months;
|
|
3811
|
+
const year = start.getUTCFullYear() + Math.floor(total / 12);
|
|
3812
|
+
const month = total % 12;
|
|
3813
|
+
const day = Math.min(start.getUTCDate(), daysInMonth(year, month));
|
|
3814
|
+
return Date.UTC(
|
|
3815
|
+
year,
|
|
3816
|
+
month,
|
|
3817
|
+
day,
|
|
3818
|
+
start.getUTCHours(),
|
|
3819
|
+
start.getUTCMinutes(),
|
|
3820
|
+
start.getUTCSeconds(),
|
|
3821
|
+
start.getUTCMilliseconds()
|
|
3822
|
+
);
|
|
3823
|
+
};
|
|
3783
3824
|
var FlexAPI = class {
|
|
3784
3825
|
app;
|
|
3785
3826
|
sqlite;
|
|
@@ -3811,6 +3852,7 @@ var FlexAPI = class {
|
|
|
3811
3852
|
RefundCheckoutSession: (context) => this.idempotent(context, () => this.refund(context)),
|
|
3812
3853
|
CreateCustomer: (context) => this.idempotent(context, () => this.createCustomer(context)),
|
|
3813
3854
|
GetSetupIntent: (context) => this.getSetupIntent(context),
|
|
3855
|
+
GetSubscription: (context) => this.getSubscription(context),
|
|
3814
3856
|
HostedCheckoutPage: (context) => this.hostedPage(context),
|
|
3815
3857
|
SubmitHostedCheckout: (context) => this.submitHosted(context),
|
|
3816
3858
|
CancelHostedCheckout: (context) => this.cancelHosted(context)
|
|
@@ -3977,6 +4019,12 @@ var FlexAPI = class {
|
|
|
3977
4019
|
if (mode !== "setup" && lines.length === 0) {
|
|
3978
4020
|
return invalid("At least one line item is required.", "line_items");
|
|
3979
4021
|
}
|
|
4022
|
+
if (mode === "subscription" && !lines.some((line) => line.price_data.recurring)) {
|
|
4023
|
+
return invalid(
|
|
4024
|
+
"Subscription mode requires at least one line item with a recurring price.",
|
|
4025
|
+
"line_items"
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
3980
4028
|
if (mode === "off_session" && (!customerId || !paymentMethodId)) {
|
|
3981
4029
|
return invalid("Off-session mode requires a customer and a payment method.", "mode");
|
|
3982
4030
|
}
|
|
@@ -4008,7 +4056,14 @@ var FlexAPI = class {
|
|
|
4008
4056
|
products.push(product);
|
|
4009
4057
|
}
|
|
4010
4058
|
const items = lines.map((line) => ({
|
|
4011
|
-
price_data: {
|
|
4059
|
+
price_data: {
|
|
4060
|
+
product: line.price_data.product,
|
|
4061
|
+
unit_amount: line.price_data.unit_amount,
|
|
4062
|
+
recurring: line.price_data.recurring ? {
|
|
4063
|
+
interval: line.price_data.recurring.interval,
|
|
4064
|
+
interval_count: line.price_data.recurring.interval_count ?? 1
|
|
4065
|
+
} : null
|
|
4066
|
+
},
|
|
4012
4067
|
quantity: line.quantity,
|
|
4013
4068
|
amount_total: line.price_data.unit_amount * line.quantity
|
|
4014
4069
|
}));
|
|
@@ -4061,6 +4116,8 @@ var FlexAPI = class {
|
|
|
4061
4116
|
customer: customerId,
|
|
4062
4117
|
payment_intent: null,
|
|
4063
4118
|
setup_intent: setupIntent,
|
|
4119
|
+
subscription: null,
|
|
4120
|
+
subscription_data: mode === "subscription" ? isRecord5(input.subscription_data) ? input.subscription_data : {} : null,
|
|
4064
4121
|
mode,
|
|
4065
4122
|
status: "open",
|
|
4066
4123
|
url: `${this.pageBase(context)}/pay/${encodeURIComponent(id)}`,
|
|
@@ -4249,6 +4306,49 @@ var FlexAPI = class {
|
|
|
4249
4306
|
{ ids: { setupIntentId: id } }
|
|
4250
4307
|
);
|
|
4251
4308
|
}
|
|
4309
|
+
getSubscription(context) {
|
|
4310
|
+
const id = context.params.subscriptionId ?? "";
|
|
4311
|
+
const subscription = this.state.subscriptions.get(id);
|
|
4312
|
+
if (!subscription) return error(404, "invalid_request_error", `No such subscription: '${id}'`);
|
|
4313
|
+
return annotateResponse(jsonRes(200, { subscription }), { ids: { subscriptionId: id } });
|
|
4314
|
+
}
|
|
4315
|
+
/**
|
|
4316
|
+
* Start the subscription a paid subscription-mode session buys: active, billed from now
|
|
4317
|
+
* for one period of its first recurring line item, charged to the card just used.
|
|
4318
|
+
*/
|
|
4319
|
+
startSubscription(session, paymentMethod) {
|
|
4320
|
+
const recurring = session.line_items.flatMap(
|
|
4321
|
+
(item) => item.price_data.recurring ? [
|
|
4322
|
+
{
|
|
4323
|
+
price_data: { ...item.price_data, recurring: item.price_data.recurring },
|
|
4324
|
+
quantity: item.quantity
|
|
4325
|
+
}
|
|
4326
|
+
] : []
|
|
4327
|
+
);
|
|
4328
|
+
const first = recurring[0]?.price_data.recurring ?? { interval: "month" };
|
|
4329
|
+
const data = session.subscription_data ?? {};
|
|
4330
|
+
const subscription = {
|
|
4331
|
+
subscription_id: this.state.nextId("fsub_"),
|
|
4332
|
+
status: "active",
|
|
4333
|
+
items: recurring,
|
|
4334
|
+
customer: session.customer,
|
|
4335
|
+
default_payment_method: paymentMethod,
|
|
4336
|
+
cancel_at_period_end: data.cancel_at_period_end === true,
|
|
4337
|
+
current_period_start: this.iso(),
|
|
4338
|
+
current_period_end: new Date(periodEnd(this.now(), first)).toISOString(),
|
|
4339
|
+
canceled_at: null,
|
|
4340
|
+
metadata: data.metadata ?? null,
|
|
4341
|
+
test_mode: session.test_mode,
|
|
4342
|
+
created_at: this.iso()
|
|
4343
|
+
};
|
|
4344
|
+
this.state.subscriptions.insert(subscription.subscription_id, subscription);
|
|
4345
|
+
this.emit("customer.subscription.created", { ...subscription }, session.test_mode);
|
|
4346
|
+
return subscription;
|
|
4347
|
+
}
|
|
4348
|
+
/** The subscriptions paid subscription-mode sessions created in this namespace. */
|
|
4349
|
+
subscriptions() {
|
|
4350
|
+
return this.state.subscriptions.list().map((row) => row.value);
|
|
4351
|
+
}
|
|
4252
4352
|
// --- lifecycle (hosted page, admin, off-session) ----------------------------------------
|
|
4253
4353
|
paymentIntentFor(session) {
|
|
4254
4354
|
const existing = session.payment_intent ? this.state.paymentIntents.get(session.payment_intent) : void 0;
|
|
@@ -4328,11 +4428,13 @@ var FlexAPI = class {
|
|
|
4328
4428
|
const before = this.paymentIntentFor(session);
|
|
4329
4429
|
const wasProcessing = before.status === "processing";
|
|
4330
4430
|
session = this.state.sessions.get(id);
|
|
4431
|
+
const subscription = session.mode === "subscription" ? this.startSubscription(session, method.payment_method_id) : void 0;
|
|
4331
4432
|
this.state.sessions.update(id, {
|
|
4332
4433
|
...session,
|
|
4333
4434
|
status: "complete",
|
|
4334
4435
|
amount_received: session.amount_total,
|
|
4335
|
-
next_action: null
|
|
4436
|
+
next_action: null,
|
|
4437
|
+
...subscription ? { subscription: subscription.subscription_id } : {}
|
|
4336
4438
|
});
|
|
4337
4439
|
this.setPaymentIntent(id, {
|
|
4338
4440
|
status: "succeeded",
|
|
@@ -4533,6 +4635,7 @@ export {
|
|
|
4533
4635
|
ELIGIBILITIES,
|
|
4534
4636
|
NEXT_ACTION_TYPES,
|
|
4535
4637
|
PAYMENT_INTENT_STATUSES,
|
|
4638
|
+
SUBSCRIPTION_STATUSES,
|
|
4536
4639
|
DEFAULT_SETTINGS,
|
|
4537
4640
|
corpusProduct,
|
|
4538
4641
|
FLEX_PRESETS,
|
|
@@ -4540,7 +4643,8 @@ export {
|
|
|
4540
4643
|
FLEX_NAMESPACE,
|
|
4541
4644
|
FLEX_EVENT_TYPES,
|
|
4542
4645
|
keyMode,
|
|
4646
|
+
periodEnd,
|
|
4543
4647
|
FlexAPI,
|
|
4544
4648
|
isNextActionType
|
|
4545
4649
|
};
|
|
4546
|
-
//# sourceMappingURL=chunk-
|
|
4650
|
+
//# sourceMappingURL=chunk-CTPIFEBJ.js.map
|