@stardeck-customer-apps/testing 0.26.0 → 0.28.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/SKILL.md +51 -1
- package/dist/index.d.mts +117 -2
- package/dist/index.d.ts +117 -2
- package/dist/index.js +569 -17
- package/dist/index.mjs +569 -17
- package/dist/next/headers-shim.js +13 -0
- package/dist/next/headers-shim.mjs +13 -0
- package/dist/setup.js +418 -16
- package/dist/setup.mjs +418 -16
- package/package.json +10 -3
package/SKILL.md
CHANGED
|
@@ -216,6 +216,23 @@ app.payments.failNext("stripe.getCheckoutSessionStatus", {
|
|
|
216
216
|
// Beam payment links reject idempotencyKey ("idempotency_unsupported") and echo expiresAt.
|
|
217
217
|
// The simulator does not model Stripe's 24 h key retention.
|
|
218
218
|
|
|
219
|
+
// Stripe inline payments (payments-sdk >= 4.12.0): PaymentIntents, same routes and shapes as
|
|
220
|
+
// the platform. Card → clientSecret + actionRequired "CLIENT_CONFIRM"; PromptPay (THB only) →
|
|
221
|
+
// qr.imageUrlPng + "ENCODED_IMAGE". Events are built from the stored payment, so the webhook
|
|
222
|
+
// carries the amount, currency and metadata (incl. stardeckReferenceId, projectId) the app sent.
|
|
223
|
+
const pi = await payments.createStripePaymentIntent({
|
|
224
|
+
amount: 12000, currency: "thb", method: "promptpay",
|
|
225
|
+
referenceId: order.id, idempotencyKey: `attempt-${order.id}`, metadata: { orderId: order.id },
|
|
226
|
+
});
|
|
227
|
+
expect(pi.qr?.imageUrlPng).toBeTruthy();
|
|
228
|
+
app.payments.markPaid(pi.id); // the shopper scanned and paid
|
|
229
|
+
await app.payments.deliverStripeEvent(POST, { paymentIntentId: pi.id }); // payment_intent.succeeded
|
|
230
|
+
// app.payments.failPayment(pi.id, "card_declined") → payment_intent.payment_failed (retryable)
|
|
231
|
+
// app.payments.cancelPayment(pi.id) → payment_intent.canceled
|
|
232
|
+
app.payments.setStripePaymentMethods(["card"]); // merchant turned PromptPay off:
|
|
233
|
+
await expect(payments.createStripePaymentIntent({ ...req, method: "promptpay" }))
|
|
234
|
+
.rejects.toMatchObject({ statusCode: 422, code: "method_not_allowed" });
|
|
235
|
+
|
|
219
236
|
// File upload
|
|
220
237
|
const storage = new StorageClient();
|
|
221
238
|
await storage.upload(new File(["hi"], "note.txt", { type: "text/plain" }));
|
|
@@ -437,6 +454,19 @@ externalId })` helper models a trusted platform/channel link, and
|
|
|
437
454
|
`.setSessionStatus(id, status)`, `.setPaymentLinkStatus(id, status)`,
|
|
438
455
|
`.failNext(operation, failure)`,
|
|
439
456
|
`.deliverStripeEvent(handler, event)`, `.deliverBeamEvent(handler, event)`,
|
|
457
|
+
Stripe inline payments: `.stripePaymentIntents`, `.getStripePaymentIntent(id)`,
|
|
458
|
+
`.markPaid(piId)` (succeeds the PaymentIntent), `.failPayment(piId, code = "card_declined")`
|
|
459
|
+
(back to `requires_payment_method` with `lastPaymentError`), `.cancelPayment(piId)`,
|
|
460
|
+
`.deliverStripeEvent(handler, { paymentIntentId })` (sends `payment_intent.succeeded`,
|
|
461
|
+
`.payment_failed` or `.canceled` for the payment's current state, built from the stored
|
|
462
|
+
payment), `.setStripePaymentMethods(methods)` (the merchant's allowed methods, default
|
|
463
|
+
`["card", "promptpay"]`; readiness lists only these and create refuses others with 422
|
|
464
|
+
`method_not_allowed`), `.setStripeChargesEnabled(false)` (readiness not ready, create → 503
|
|
465
|
+
`account_not_ready`). Validation matches the platform (400 `invalid_request`, PromptPay
|
|
466
|
+
non-THB → 422 `currency_not_supported`, idempotent create, 409 `idempotency_conflict`, 409
|
|
467
|
+
`already_succeeded` on cancel after payment), and another project's payment id → 404
|
|
468
|
+
`not_found`. The app's `POST /api/payments/payment-intents` route refuses card (403
|
|
469
|
+
`CARD_CHARGE_SERVER_ONLY`); create card payments from server code.
|
|
440
470
|
Bolt+ controls: `.approveBoltIntent(id)`, `.declineBoltIntent(id)`,
|
|
441
471
|
`.expireBoltIntent(id)`, `.listBoltIntents()`, `.getBoltIntent(id)`,
|
|
442
472
|
`.count`, `.clear()`. Approve marks PAID and records a charge; deliver the
|
|
@@ -541,7 +571,7 @@ true })` remains source-compatible but the simulator (like the control plane)
|
|
|
541
571
|
- For merge-correct reads, request aliases in batches and include every id in
|
|
542
572
|
the returned canonical set. Page directory UIs through `search({ query,
|
|
543
573
|
cursor, limit })`; `list()` intentionally keeps its previous array shape.
|
|
544
|
-
- `PaymentsServerClient` (Stripe checkout + Beam payment links + Beam direct
|
|
574
|
+
- `PaymentsServerClient` (Stripe checkout + Stripe inline payments + Beam payment links + Beam direct
|
|
545
575
|
charges and card tokenization + Bolt+ connections/intents/charges, product list) — captured in `app.payments`;
|
|
546
576
|
fulfill checkout via `markPaid` (poll) or `deliverStripeEvent` /
|
|
547
577
|
`deliverBeamEvent` (webhook push). Drive a terminal without hardware via
|
|
@@ -556,6 +586,8 @@ cursor, limit })`; `list()` intentionally keeps its previous array shape.
|
|
|
556
586
|
one-shot HTTP or network failures during Stripe checkout creation/status polling
|
|
557
587
|
and Beam payment-link creation/status polling. The operation names are
|
|
558
588
|
`stripe.createCheckout`, `stripe.getCheckoutSessionStatus`,
|
|
589
|
+
`stripe.createPaymentIntent`, `stripe.getPaymentIntent`, `stripe.findPaymentIntents`,
|
|
590
|
+
`stripe.cancelPaymentIntent`, `stripe.getElementsConfig`, `stripe.getReadiness`,
|
|
559
591
|
`beam.createPaymentLink`, `beam.getPaymentLinkStatus`, `beam.createDirectCharge`,
|
|
560
592
|
and `beam.getCharge`. HTTP failures support
|
|
561
593
|
statuses 400, 402, 409, 429, 500, and 503 plus optional `code`, `details`, and
|
|
@@ -592,6 +624,24 @@ cursor, limit })`; `list()` intentionally keeps its previous array shape.
|
|
|
592
624
|
`expect(app.schedules.earliestWake("booking-janitor")!.getTime()).toBeLessThanOrEqual(dueAt.getTime())`.
|
|
593
625
|
`app.schedules.wakesFor(key)` lists them; `reset()` clears them. Run the job's handler
|
|
594
626
|
yourself to test what it does and the `nextRun()` it returns.
|
|
627
|
+
- `getControl` / `getControls` / `setControl` from `@stardeck-customer-apps/controls/server` — the
|
|
628
|
+
sync (`POST /api/deployments/:id/controls`) and write (`PUT .../controls/:key`) routes are
|
|
629
|
+
simulated, signature-checked like the others, and served from `app.controls`. Defaults are
|
|
630
|
+
permissive so existing tests keep passing: `flag.paymentsSetupStep: false`,
|
|
631
|
+
`plan.locations: { included: 3, max: null, extraMonthly: { amount: 990, currency: "THB" } }`,
|
|
632
|
+
`plan.multiEntity: true`, `payments.provider: "beam"`, `payments.methods: { card: true, promptPay: true }`.
|
|
633
|
+
Seed with `createTestApp({ controls: { "plan.multiEntity": false } })`. Then
|
|
634
|
+
`app.controls.set(key, value)` (the next `getControl` in the same process sees it: the
|
|
635
|
+
controls cache is dropped on every change and on `reset()`),
|
|
636
|
+
`app.controls.setUnavailable(key)` (that key reads `{ ok: false }`, like a platform outage),
|
|
637
|
+
`app.controls.lastUsage()` (the usage the app reported on its latest sync, for example
|
|
638
|
+
`{ locations: 4 }`), `app.controls.writes()` (each `setControl` the app made:
|
|
639
|
+
`{ key, value, userToken }`) and `app.controls.refuseNextWrite({ code, message, connectUrl? })`
|
|
640
|
+
(the next write fails once with that error, for example `PROVIDER_NOT_READY` with a connect
|
|
641
|
+
link). A write needs the person's session token in `X-Stardeck-User-Token`; without it the
|
|
642
|
+
simulator answers 401 `UNAUTHENTICATED`. Only `payments.provider` and `payments.methods` are
|
|
643
|
+
writable (anything else is 400 `NOT_WRITABLE`), and an invalid value is 400 `INVALID_VALUE`.
|
|
644
|
+
`app.controls.clear()` returns to the seeded values. Test the `ok: false` path of every gate.
|
|
595
645
|
|
|
596
646
|
### Receipt layout and printer capabilities
|
|
597
647
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as _stardeck_customer_apps_payments_sdk from '@stardeck-customer-apps/payments-sdk';
|
|
2
2
|
import * as next_server from 'next/server';
|
|
3
|
+
import { ControlKey, ControlValue, ControlsUsage, ControlWriteErrorCode } from '@stardeck-customer-apps/controls';
|
|
3
4
|
import { PGlite } from '@electric-sql/pglite';
|
|
4
5
|
import { T as TestDataStoreInput } from './data-store-manifest-Zwgcv0DJ.mjs';
|
|
5
6
|
export { S as STARDECK_DATA_STORES_ENV, a as TestDataStoreManifestEntry, b as buildTestDataStoreManifest } from './data-store-manifest-Zwgcv0DJ.mjs';
|
|
@@ -216,10 +217,10 @@ interface CapturedCheckout {
|
|
|
216
217
|
metadata?: Record<string, string>;
|
|
217
218
|
createdAt: Date;
|
|
218
219
|
}
|
|
219
|
-
type PaymentSimulatorOperation = "stripe.createCheckout" | "stripe.getCheckoutSessionStatus" | "beam.createPaymentLink" | "beam.getPaymentLinkStatus" | "beam.createDirectCharge" | "beam.getCharge";
|
|
220
|
+
type PaymentSimulatorOperation = "stripe.createCheckout" | "stripe.getCheckoutSessionStatus" | "beam.createPaymentLink" | "beam.getPaymentLinkStatus" | "beam.createDirectCharge" | "beam.getCharge" | "stripe.createPaymentIntent" | "stripe.getPaymentIntent" | "stripe.findPaymentIntents" | "stripe.cancelPaymentIntent" | "stripe.getElementsConfig" | "stripe.getReadiness";
|
|
220
221
|
type PaymentSimulatorFailure = {
|
|
221
222
|
kind?: "http";
|
|
222
|
-
status: 400 | 402 | 409 | 429 | 500 | 503;
|
|
223
|
+
status: 400 | 402 | 404 | 409 | 422 | 429 | 500 | 502 | 503;
|
|
223
224
|
message: string;
|
|
224
225
|
code?: string;
|
|
225
226
|
details?: unknown;
|
|
@@ -295,19 +296,98 @@ interface SimDirectCharge {
|
|
|
295
296
|
idempotencyKey: string | null;
|
|
296
297
|
createdAt: string;
|
|
297
298
|
}
|
|
299
|
+
/** Inline Stripe payment methods (mirrors the payments SDK's `StripePaymentMethod`). */
|
|
300
|
+
type SimStripePaymentMethod = "card" | "promptpay";
|
|
301
|
+
/** A Stripe PaymentIntent as the simulator holds it. */
|
|
302
|
+
interface SimStripePaymentIntent {
|
|
303
|
+
id: string;
|
|
304
|
+
/** The project whose signed request created it; other projects get 404. */
|
|
305
|
+
projectId: string;
|
|
306
|
+
/** The account selector from the request path (`acct_…`, "default", "playground"). */
|
|
307
|
+
account: string;
|
|
308
|
+
/** The connected account it lives on; null on the playground. */
|
|
309
|
+
connectedAccountId: string | null;
|
|
310
|
+
playground: boolean;
|
|
311
|
+
status: "requires_payment_method" | "requires_confirmation" | "requires_action" | "processing" | "requires_capture" | "canceled" | "succeeded";
|
|
312
|
+
amount: number;
|
|
313
|
+
/** Lowercase. */
|
|
314
|
+
currency: string;
|
|
315
|
+
method: SimStripePaymentMethod;
|
|
316
|
+
referenceId: string;
|
|
317
|
+
/** The app's metadata, with Stardeck's reserved keys removed. */
|
|
318
|
+
appMetadata: Record<string, string>;
|
|
319
|
+
/** What Stripe holds: app metadata plus `projectId` and `stardeckReferenceId`. */
|
|
320
|
+
metadata: Record<string, string>;
|
|
321
|
+
description: string | null;
|
|
322
|
+
receiptEmail: string | null;
|
|
323
|
+
returnUrl: string | null;
|
|
324
|
+
clientSecret: string;
|
|
325
|
+
qr: {
|
|
326
|
+
imageUrlPng: string;
|
|
327
|
+
imageUrlSvg: string | null;
|
|
328
|
+
data: string | null;
|
|
329
|
+
hostedInstructionsUrl: string | null;
|
|
330
|
+
expiresAt: number | null;
|
|
331
|
+
} | null;
|
|
332
|
+
latestChargeId: string | null;
|
|
333
|
+
amountRefunded: number;
|
|
334
|
+
lastPaymentError: {
|
|
335
|
+
code: string | null;
|
|
336
|
+
declineCode: string | null;
|
|
337
|
+
message: string | null;
|
|
338
|
+
} | null;
|
|
339
|
+
/** Unix seconds. */
|
|
340
|
+
created: number;
|
|
341
|
+
}
|
|
298
342
|
interface TestPayments {
|
|
299
343
|
get checkouts(): CapturedCheckout[];
|
|
300
344
|
latest(): CapturedCheckout | undefined;
|
|
301
345
|
setProducts(products: _stardeck_customer_apps_payments_sdk.Product[]): void;
|
|
302
346
|
/** Fail the next matching SDK request once, then resume normal simulator behavior. */
|
|
303
347
|
failNext(operation: PaymentSimulatorOperation, failure: PaymentSimulatorFailure): void;
|
|
348
|
+
/**
|
|
349
|
+
* The customer paid. Takes a Stripe Checkout session id, a Beam payment link
|
|
350
|
+
* id, or a Stripe PaymentIntent id (`pi_sim_…`). For a PaymentIntent, follow
|
|
351
|
+
* with `deliverStripeEvent(handler, { paymentIntentId })` to send the
|
|
352
|
+
* `payment_intent.succeeded` webhook.
|
|
353
|
+
*/
|
|
304
354
|
markPaid(id: string): void;
|
|
355
|
+
/**
|
|
356
|
+
* A Stripe PaymentIntent's payment attempt failed with `code` (default
|
|
357
|
+
* "card_declined"). It goes back to `requires_payment_method` with
|
|
358
|
+
* `lastPaymentError` set; deliver `payment_intent.payment_failed` with
|
|
359
|
+
* `deliverStripeEvent(handler, { paymentIntentId })`.
|
|
360
|
+
*/
|
|
361
|
+
failPayment(paymentIntentId: string, code?: string): void;
|
|
362
|
+
/**
|
|
363
|
+
* The Stripe PaymentIntent was canceled outside the app (expired QR, merchant
|
|
364
|
+
* dashboard). Deliver `payment_intent.canceled` with `deliverStripeEvent`.
|
|
365
|
+
*/
|
|
366
|
+
cancelPayment(paymentIntentId: string): void;
|
|
367
|
+
/** Every Stripe PaymentIntent created so far, oldest first, as stored. */
|
|
368
|
+
get stripePaymentIntents(): SimStripePaymentIntent[];
|
|
369
|
+
getStripePaymentIntent(paymentIntentId: string): SimStripePaymentIntent | undefined;
|
|
370
|
+
/**
|
|
371
|
+
* The inline Stripe methods the merchant allows (default `["card", "promptpay"]`).
|
|
372
|
+
* Create refuses others with 422 `method_not_allowed`; readiness lists only these.
|
|
373
|
+
*/
|
|
374
|
+
setStripePaymentMethods(methods: SimStripePaymentMethod[]): void;
|
|
375
|
+
/** Turn Stripe charges off (readiness not ready; create → 503 `account_not_ready`). On by default. */
|
|
376
|
+
setStripeChargesEnabled(enabled: boolean): void;
|
|
305
377
|
setSessionStatus(id: string, status: Partial<_stardeck_customer_apps_payments_sdk.CheckoutSessionStatus>): void;
|
|
306
378
|
setPaymentLinkStatus(id: string, status: _stardeck_customer_apps_payments_sdk.BeamPaymentLinkStatus): void;
|
|
379
|
+
/**
|
|
380
|
+
* Deliver a Stripe webhook to the app's payments route, signed like the
|
|
381
|
+
* platform. Pass `{ paymentIntentId }` to send the `payment_intent.*` event
|
|
382
|
+
* for that PaymentIntent's current state, built from the stored intent
|
|
383
|
+
* (amount, currency, metadata incl. `stardeckReferenceId` and `projectId`).
|
|
384
|
+
*/
|
|
307
385
|
deliverStripeEvent(handler: PaymentsWebhookHandler, event: {
|
|
308
386
|
type: string;
|
|
309
387
|
data: unknown;
|
|
310
388
|
accountId?: string;
|
|
389
|
+
} | {
|
|
390
|
+
paymentIntentId: string;
|
|
311
391
|
}, options?: {
|
|
312
392
|
path?: string;
|
|
313
393
|
deploymentSecret?: string;
|
|
@@ -666,7 +746,40 @@ interface TestSchedules {
|
|
|
666
746
|
earliestWake(scheduleKey: string): Date | undefined;
|
|
667
747
|
clear(): void;
|
|
668
748
|
}
|
|
749
|
+
interface CapturedControlWrite {
|
|
750
|
+
key: ControlKey;
|
|
751
|
+
value: unknown;
|
|
752
|
+
/** The person's session token the app sent in `X-Stardeck-User-Token`. */
|
|
753
|
+
userToken: string;
|
|
754
|
+
}
|
|
755
|
+
interface ControlRefusal {
|
|
756
|
+
code: ControlWriteErrorCode;
|
|
757
|
+
message: string;
|
|
758
|
+
connectUrl?: string;
|
|
759
|
+
}
|
|
760
|
+
/** The simulated app-controls sync and write routes (`@stardeck-customer-apps/controls`). */
|
|
761
|
+
interface TestControls {
|
|
762
|
+
/**
|
|
763
|
+
* Set a control's value (the app sees it on its next `getControl`, with no
|
|
764
|
+
* stale cache). Also makes a key that was forced unavailable available again.
|
|
765
|
+
*/
|
|
766
|
+
set<K extends ControlKey>(key: K, value: ControlValue<K>): void;
|
|
767
|
+
/** Make the sync report this control as `{ ok: false }`, like a platform outage for one key. */
|
|
768
|
+
setUnavailable(key: ControlKey): void;
|
|
769
|
+
/** The usage the app reported on its latest sync, or null if it reported none. */
|
|
770
|
+
lastUsage(): ControlsUsage | null;
|
|
771
|
+
/** Writes the app made through `setControl`, oldest first. */
|
|
772
|
+
writes(): CapturedControlWrite[];
|
|
773
|
+
/** The next write is refused with this error, then writes succeed again. */
|
|
774
|
+
refuseNextWrite(refusal: ControlRefusal): void;
|
|
775
|
+
/** Back to the values the test app was created with; clears writes, usage and refusals. */
|
|
776
|
+
clear(): void;
|
|
777
|
+
}
|
|
669
778
|
interface TestAppOptions {
|
|
779
|
+
/** Control values the simulated platform serves; unlisted keys keep the permissive defaults. */
|
|
780
|
+
controls?: Partial<{
|
|
781
|
+
[K in ControlKey]: ControlValue<K>;
|
|
782
|
+
}>;
|
|
670
783
|
/**
|
|
671
784
|
* The LIFF ID the platform forwards to the app for a LINE MINI App shop. It is
|
|
672
785
|
* what `GET /api/auth/me` returns as `liffId` and what makes `useLiff()` start
|
|
@@ -720,6 +833,8 @@ interface TestApp {
|
|
|
720
833
|
starlens: TestStarlens;
|
|
721
834
|
/** Wakes the app asked of its adaptive schedules (`wakeSchedule`). */
|
|
722
835
|
schedules: TestSchedules;
|
|
836
|
+
/** Platform-owned app controls: flags, plan limits, payment provider and methods. */
|
|
837
|
+
controls: TestControls;
|
|
723
838
|
/** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
|
|
724
839
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
725
840
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as _stardeck_customer_apps_payments_sdk from '@stardeck-customer-apps/payments-sdk';
|
|
2
2
|
import * as next_server from 'next/server';
|
|
3
|
+
import { ControlKey, ControlValue, ControlsUsage, ControlWriteErrorCode } from '@stardeck-customer-apps/controls';
|
|
3
4
|
import { PGlite } from '@electric-sql/pglite';
|
|
4
5
|
import { T as TestDataStoreInput } from './data-store-manifest-Zwgcv0DJ.js';
|
|
5
6
|
export { S as STARDECK_DATA_STORES_ENV, a as TestDataStoreManifestEntry, b as buildTestDataStoreManifest } from './data-store-manifest-Zwgcv0DJ.js';
|
|
@@ -216,10 +217,10 @@ interface CapturedCheckout {
|
|
|
216
217
|
metadata?: Record<string, string>;
|
|
217
218
|
createdAt: Date;
|
|
218
219
|
}
|
|
219
|
-
type PaymentSimulatorOperation = "stripe.createCheckout" | "stripe.getCheckoutSessionStatus" | "beam.createPaymentLink" | "beam.getPaymentLinkStatus" | "beam.createDirectCharge" | "beam.getCharge";
|
|
220
|
+
type PaymentSimulatorOperation = "stripe.createCheckout" | "stripe.getCheckoutSessionStatus" | "beam.createPaymentLink" | "beam.getPaymentLinkStatus" | "beam.createDirectCharge" | "beam.getCharge" | "stripe.createPaymentIntent" | "stripe.getPaymentIntent" | "stripe.findPaymentIntents" | "stripe.cancelPaymentIntent" | "stripe.getElementsConfig" | "stripe.getReadiness";
|
|
220
221
|
type PaymentSimulatorFailure = {
|
|
221
222
|
kind?: "http";
|
|
222
|
-
status: 400 | 402 | 409 | 429 | 500 | 503;
|
|
223
|
+
status: 400 | 402 | 404 | 409 | 422 | 429 | 500 | 502 | 503;
|
|
223
224
|
message: string;
|
|
224
225
|
code?: string;
|
|
225
226
|
details?: unknown;
|
|
@@ -295,19 +296,98 @@ interface SimDirectCharge {
|
|
|
295
296
|
idempotencyKey: string | null;
|
|
296
297
|
createdAt: string;
|
|
297
298
|
}
|
|
299
|
+
/** Inline Stripe payment methods (mirrors the payments SDK's `StripePaymentMethod`). */
|
|
300
|
+
type SimStripePaymentMethod = "card" | "promptpay";
|
|
301
|
+
/** A Stripe PaymentIntent as the simulator holds it. */
|
|
302
|
+
interface SimStripePaymentIntent {
|
|
303
|
+
id: string;
|
|
304
|
+
/** The project whose signed request created it; other projects get 404. */
|
|
305
|
+
projectId: string;
|
|
306
|
+
/** The account selector from the request path (`acct_…`, "default", "playground"). */
|
|
307
|
+
account: string;
|
|
308
|
+
/** The connected account it lives on; null on the playground. */
|
|
309
|
+
connectedAccountId: string | null;
|
|
310
|
+
playground: boolean;
|
|
311
|
+
status: "requires_payment_method" | "requires_confirmation" | "requires_action" | "processing" | "requires_capture" | "canceled" | "succeeded";
|
|
312
|
+
amount: number;
|
|
313
|
+
/** Lowercase. */
|
|
314
|
+
currency: string;
|
|
315
|
+
method: SimStripePaymentMethod;
|
|
316
|
+
referenceId: string;
|
|
317
|
+
/** The app's metadata, with Stardeck's reserved keys removed. */
|
|
318
|
+
appMetadata: Record<string, string>;
|
|
319
|
+
/** What Stripe holds: app metadata plus `projectId` and `stardeckReferenceId`. */
|
|
320
|
+
metadata: Record<string, string>;
|
|
321
|
+
description: string | null;
|
|
322
|
+
receiptEmail: string | null;
|
|
323
|
+
returnUrl: string | null;
|
|
324
|
+
clientSecret: string;
|
|
325
|
+
qr: {
|
|
326
|
+
imageUrlPng: string;
|
|
327
|
+
imageUrlSvg: string | null;
|
|
328
|
+
data: string | null;
|
|
329
|
+
hostedInstructionsUrl: string | null;
|
|
330
|
+
expiresAt: number | null;
|
|
331
|
+
} | null;
|
|
332
|
+
latestChargeId: string | null;
|
|
333
|
+
amountRefunded: number;
|
|
334
|
+
lastPaymentError: {
|
|
335
|
+
code: string | null;
|
|
336
|
+
declineCode: string | null;
|
|
337
|
+
message: string | null;
|
|
338
|
+
} | null;
|
|
339
|
+
/** Unix seconds. */
|
|
340
|
+
created: number;
|
|
341
|
+
}
|
|
298
342
|
interface TestPayments {
|
|
299
343
|
get checkouts(): CapturedCheckout[];
|
|
300
344
|
latest(): CapturedCheckout | undefined;
|
|
301
345
|
setProducts(products: _stardeck_customer_apps_payments_sdk.Product[]): void;
|
|
302
346
|
/** Fail the next matching SDK request once, then resume normal simulator behavior. */
|
|
303
347
|
failNext(operation: PaymentSimulatorOperation, failure: PaymentSimulatorFailure): void;
|
|
348
|
+
/**
|
|
349
|
+
* The customer paid. Takes a Stripe Checkout session id, a Beam payment link
|
|
350
|
+
* id, or a Stripe PaymentIntent id (`pi_sim_…`). For a PaymentIntent, follow
|
|
351
|
+
* with `deliverStripeEvent(handler, { paymentIntentId })` to send the
|
|
352
|
+
* `payment_intent.succeeded` webhook.
|
|
353
|
+
*/
|
|
304
354
|
markPaid(id: string): void;
|
|
355
|
+
/**
|
|
356
|
+
* A Stripe PaymentIntent's payment attempt failed with `code` (default
|
|
357
|
+
* "card_declined"). It goes back to `requires_payment_method` with
|
|
358
|
+
* `lastPaymentError` set; deliver `payment_intent.payment_failed` with
|
|
359
|
+
* `deliverStripeEvent(handler, { paymentIntentId })`.
|
|
360
|
+
*/
|
|
361
|
+
failPayment(paymentIntentId: string, code?: string): void;
|
|
362
|
+
/**
|
|
363
|
+
* The Stripe PaymentIntent was canceled outside the app (expired QR, merchant
|
|
364
|
+
* dashboard). Deliver `payment_intent.canceled` with `deliverStripeEvent`.
|
|
365
|
+
*/
|
|
366
|
+
cancelPayment(paymentIntentId: string): void;
|
|
367
|
+
/** Every Stripe PaymentIntent created so far, oldest first, as stored. */
|
|
368
|
+
get stripePaymentIntents(): SimStripePaymentIntent[];
|
|
369
|
+
getStripePaymentIntent(paymentIntentId: string): SimStripePaymentIntent | undefined;
|
|
370
|
+
/**
|
|
371
|
+
* The inline Stripe methods the merchant allows (default `["card", "promptpay"]`).
|
|
372
|
+
* Create refuses others with 422 `method_not_allowed`; readiness lists only these.
|
|
373
|
+
*/
|
|
374
|
+
setStripePaymentMethods(methods: SimStripePaymentMethod[]): void;
|
|
375
|
+
/** Turn Stripe charges off (readiness not ready; create → 503 `account_not_ready`). On by default. */
|
|
376
|
+
setStripeChargesEnabled(enabled: boolean): void;
|
|
305
377
|
setSessionStatus(id: string, status: Partial<_stardeck_customer_apps_payments_sdk.CheckoutSessionStatus>): void;
|
|
306
378
|
setPaymentLinkStatus(id: string, status: _stardeck_customer_apps_payments_sdk.BeamPaymentLinkStatus): void;
|
|
379
|
+
/**
|
|
380
|
+
* Deliver a Stripe webhook to the app's payments route, signed like the
|
|
381
|
+
* platform. Pass `{ paymentIntentId }` to send the `payment_intent.*` event
|
|
382
|
+
* for that PaymentIntent's current state, built from the stored intent
|
|
383
|
+
* (amount, currency, metadata incl. `stardeckReferenceId` and `projectId`).
|
|
384
|
+
*/
|
|
307
385
|
deliverStripeEvent(handler: PaymentsWebhookHandler, event: {
|
|
308
386
|
type: string;
|
|
309
387
|
data: unknown;
|
|
310
388
|
accountId?: string;
|
|
389
|
+
} | {
|
|
390
|
+
paymentIntentId: string;
|
|
311
391
|
}, options?: {
|
|
312
392
|
path?: string;
|
|
313
393
|
deploymentSecret?: string;
|
|
@@ -666,7 +746,40 @@ interface TestSchedules {
|
|
|
666
746
|
earliestWake(scheduleKey: string): Date | undefined;
|
|
667
747
|
clear(): void;
|
|
668
748
|
}
|
|
749
|
+
interface CapturedControlWrite {
|
|
750
|
+
key: ControlKey;
|
|
751
|
+
value: unknown;
|
|
752
|
+
/** The person's session token the app sent in `X-Stardeck-User-Token`. */
|
|
753
|
+
userToken: string;
|
|
754
|
+
}
|
|
755
|
+
interface ControlRefusal {
|
|
756
|
+
code: ControlWriteErrorCode;
|
|
757
|
+
message: string;
|
|
758
|
+
connectUrl?: string;
|
|
759
|
+
}
|
|
760
|
+
/** The simulated app-controls sync and write routes (`@stardeck-customer-apps/controls`). */
|
|
761
|
+
interface TestControls {
|
|
762
|
+
/**
|
|
763
|
+
* Set a control's value (the app sees it on its next `getControl`, with no
|
|
764
|
+
* stale cache). Also makes a key that was forced unavailable available again.
|
|
765
|
+
*/
|
|
766
|
+
set<K extends ControlKey>(key: K, value: ControlValue<K>): void;
|
|
767
|
+
/** Make the sync report this control as `{ ok: false }`, like a platform outage for one key. */
|
|
768
|
+
setUnavailable(key: ControlKey): void;
|
|
769
|
+
/** The usage the app reported on its latest sync, or null if it reported none. */
|
|
770
|
+
lastUsage(): ControlsUsage | null;
|
|
771
|
+
/** Writes the app made through `setControl`, oldest first. */
|
|
772
|
+
writes(): CapturedControlWrite[];
|
|
773
|
+
/** The next write is refused with this error, then writes succeed again. */
|
|
774
|
+
refuseNextWrite(refusal: ControlRefusal): void;
|
|
775
|
+
/** Back to the values the test app was created with; clears writes, usage and refusals. */
|
|
776
|
+
clear(): void;
|
|
777
|
+
}
|
|
669
778
|
interface TestAppOptions {
|
|
779
|
+
/** Control values the simulated platform serves; unlisted keys keep the permissive defaults. */
|
|
780
|
+
controls?: Partial<{
|
|
781
|
+
[K in ControlKey]: ControlValue<K>;
|
|
782
|
+
}>;
|
|
670
783
|
/**
|
|
671
784
|
* The LIFF ID the platform forwards to the app for a LINE MINI App shop. It is
|
|
672
785
|
* what `GET /api/auth/me` returns as `liffId` and what makes `useLiff()` start
|
|
@@ -720,6 +833,8 @@ interface TestApp {
|
|
|
720
833
|
starlens: TestStarlens;
|
|
721
834
|
/** Wakes the app asked of its adaptive schedules (`wakeSchedule`). */
|
|
722
835
|
schedules: TestSchedules;
|
|
836
|
+
/** Platform-owned app controls: flags, plan limits, payment provider and methods. */
|
|
837
|
+
controls: TestControls;
|
|
723
838
|
/** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
|
|
724
839
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
725
840
|
/**
|