@waffo/pancake-ts 0.1.7 → 0.1.9

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.
@@ -0,0 +1,664 @@
1
+ # GraphQL Guide
2
+
3
+ The Waffo Pancake GraphQL API is **query-only** — Mutations are not supported and return a 403 error. All queries go through `client.graphql.query<T>()`.
4
+
5
+ ## Introspection
6
+
7
+ Introspection is **enabled by default**. Use it during development to explore the full schema, discover available types, fields, and filter conditions.
8
+
9
+ > **Recommended**: Always use introspection to stay in sync with the server — this guide covers common queries, but the schema is the source of truth.
10
+
11
+ ### Discover All Query Fields
12
+
13
+ ```typescript
14
+ const schema = await client.graphql.query({
15
+ query: `{
16
+ __schema {
17
+ queryType {
18
+ fields {
19
+ name
20
+ description
21
+ args { name type { name kind ofType { name } } }
22
+ }
23
+ }
24
+ }
25
+ }`,
26
+ });
27
+ console.log(schema.data?.__schema.queryType.fields);
28
+ ```
29
+
30
+ ### Inspect a Specific Type
31
+
32
+ ```typescript
33
+ const orderType = await client.graphql.query({
34
+ query: `{
35
+ __type(name: "OnetimeOrder") {
36
+ fields {
37
+ name
38
+ type { name kind ofType { name } }
39
+ }
40
+ }
41
+ }`,
42
+ });
43
+ console.log(orderType.data?.__type.fields);
44
+ ```
45
+
46
+ ### Interactive Schema Browsers
47
+
48
+ You can also connect [GraphiQL](https://github.com/graphql/graphiql) or [Apollo Sandbox](https://studio.apollographql.com/sandbox) to `https://api.waffo.ai/v1/graphql` for interactive schema browsing with auto-complete.
49
+
50
+ ---
51
+
52
+ ## Practical Examples
53
+
54
+ ### 1. Store Queries
55
+
56
+ ```typescript
57
+ interface StoresQuery {
58
+ stores: Array<{
59
+ id: string;
60
+ name: string;
61
+ slug: string;
62
+ status: string;
63
+ supportEmail: string | null;
64
+ createdAt: string;
65
+ }>;
66
+ }
67
+ const { data } = await client.graphql.query<StoresQuery>({
68
+ query: `query { stores { id name slug status supportEmail createdAt } }`,
69
+ });
70
+
71
+ // Single store by ID
72
+ const store = await client.graphql.query({
73
+ query: `query ($id: ID!) {
74
+ store(id: $id) { id name slug status }
75
+ }`,
76
+ variables: { id: "STO_xxx" },
77
+ });
78
+ ```
79
+
80
+ ### 2. Product Queries
81
+
82
+ ```typescript
83
+ // One-time products with prices
84
+ interface ProductsQuery {
85
+ onetimeProducts: Array<{
86
+ id: string;
87
+ name: string;
88
+ status: string;
89
+ prices: Array<{ currency: string; priceInfo: { amount: string; taxCategory: string } }>;
90
+ hasProdVersion: boolean;
91
+ }>;
92
+ }
93
+ const products = await client.graphql.query<ProductsQuery>({
94
+ query: `query ($storeId: String!) {
95
+ onetimeProducts(storeId: $storeId, filter: { status: { eq: "active" } }) {
96
+ id name status
97
+ prices { currency priceInfo { amount taxCategory } }
98
+ hasProdVersion
99
+ }
100
+ }`,
101
+ variables: { storeId: "STO_xxx" },
102
+ });
103
+
104
+ // Subscription products
105
+ const subProducts = await client.graphql.query({
106
+ query: `query ($storeId: String!) {
107
+ subscriptionProducts(storeId: $storeId) {
108
+ id name billingPeriod status
109
+ prices { currency priceInfo { amount taxCategory } }
110
+ }
111
+ }`,
112
+ variables: { storeId: "STO_xxx" },
113
+ });
114
+ ```
115
+
116
+ ### 3. Order Queries
117
+
118
+ ```typescript
119
+ // One-time orders with price snapshot
120
+ interface OnetimeOrdersQuery {
121
+ onetimeOrders: Array<{
122
+ id: string;
123
+ buyerEmail: string;
124
+ currency: string;
125
+ status: string;
126
+ priceSnapshot: {
127
+ currency: string;
128
+ subtotal: string;
129
+ taxAmount: string;
130
+ total: string;
131
+ taxCategory: string;
132
+ };
133
+ onetimeProduct: { name: string };
134
+ createdAt: string;
135
+ }>;
136
+ }
137
+ const orders = await client.graphql.query<OnetimeOrdersQuery>({
138
+ query: `query ($storeId: String!) {
139
+ onetimeOrders(storeId: $storeId) {
140
+ id buyerEmail currency status
141
+ priceSnapshot { currency subtotal taxAmount total taxCategory }
142
+ onetimeProduct { name }
143
+ createdAt
144
+ }
145
+ }`,
146
+ variables: { storeId: "STO_xxx" },
147
+ });
148
+
149
+ // Subscription orders
150
+ const subOrders = await client.graphql.query({
151
+ query: `query ($storeId: String!) {
152
+ subscriptionOrders(storeId: $storeId) {
153
+ id buyerEmail status billingPeriod
154
+ priceSnapshot {
155
+ currency
156
+ regularPhase { subtotal taxAmount total taxCategory }
157
+ specialPhase { subtotal taxAmount total taxCategory }
158
+ specialPhaseDays
159
+ }
160
+ currentPeriodEnd canceledAt
161
+ }
162
+ }`,
163
+ variables: { storeId: "STO_xxx" },
164
+ });
165
+ ```
166
+
167
+ ### 4. Order Details (with Payment History)
168
+
169
+ ```typescript
170
+ // One-time order with payments and refunds
171
+ const orderDetail = await client.graphql.query({
172
+ query: `query ($id: ID!) {
173
+ onetimeOrder(id: $id) {
174
+ id buyerEmail currency status testMode
175
+ priceSnapshot { currency subtotal taxAmount total taxCategory }
176
+ billingDetail { country isBusiness postcode state businessName taxId }
177
+ onetimeProduct { id name }
178
+ productVersion { id versionNumber name }
179
+ payments {
180
+ id status refundStatus
181
+ snapshotAmountDetails { currency subtotal taxAmount total taxCategory phase }
182
+ cardInfo { brand last4 expMonth expYear }
183
+ failureReason createdAt
184
+ refunds { id status requestedAmountDetails { currency amount } createdAt }
185
+ }
186
+ createdAt updatedAt
187
+ }
188
+ }`,
189
+ variables: { id: "ORD_xxx" },
190
+ });
191
+
192
+ // Subscription order with renewal status
193
+ const subDetail = await client.graphql.query({
194
+ query: `query ($id: ID!) {
195
+ subscriptionOrder(id: $id) {
196
+ id buyerEmail status billingPeriod
197
+ priceSnapshot {
198
+ currency specialPhaseDays
199
+ specialPhase { subtotal taxAmount total taxCategory }
200
+ regularPhase { subtotal taxAmount total taxCategory }
201
+ }
202
+ billingDetail { country isBusiness }
203
+ currentPeriodStart currentPeriodEnd canceledAt
204
+ subscriptionProduct { id name }
205
+ productVersion { id versionNumber name }
206
+ payments {
207
+ id status refundStatus
208
+ snapshotAmountDetails { currency subtotal taxAmount total taxCategory phase }
209
+ createdAt
210
+ }
211
+ createdAt
212
+ }
213
+ }`,
214
+ variables: { id: "ORD_xxx" },
215
+ });
216
+ ```
217
+
218
+ ### 5. Payment and Refund Queries
219
+
220
+ ```typescript
221
+ // Payments with filters
222
+ const payments = await client.graphql.query({
223
+ query: `query {
224
+ payments(filter: { status: { eq: "succeeded" } }) {
225
+ id
226
+ onetimeOrder { id }
227
+ subscriptionOrder { id }
228
+ snapshotAmountDetails { currency subtotal taxAmount total taxCategory phase }
229
+ cardInfo { brand last4 }
230
+ status createdAt
231
+ }
232
+ }`,
233
+ });
234
+
235
+ // Refund tickets
236
+ const tickets = await client.graphql.query({
237
+ query: `query {
238
+ refundTickets(limit: 20, filter: { status: { eq: "pending" } }) {
239
+ id status reason
240
+ requestedAmountDetails { currency amount }
241
+ payment {
242
+ id status
243
+ snapshotAmountDetails { currency subtotal taxAmount total taxCategory phase }
244
+ onetimeOrder { id buyerEmail store { name } }
245
+ subscriptionOrder { id buyerEmail store { name } }
246
+ }
247
+ createdAt updatedAt
248
+ }
249
+ refundTicketsCount(filter: { status: { eq: "pending" } })
250
+ }`,
251
+ });
252
+ ```
253
+
254
+ ### 6. Merchant Info and Store Associations
255
+
256
+ ```typescript
257
+ const merchant = await client.graphql.query({
258
+ query: `query ($id: ID!) {
259
+ merchant(id: $id) {
260
+ id email name status
261
+ storeMerchants {
262
+ role
263
+ store { id name slug status }
264
+ }
265
+ apiKeys { id nickname environment recentlyUsed createdAt }
266
+ }
267
+ }`,
268
+ variables: { id: "MER_xxx" },
269
+ });
270
+ ```
271
+
272
+ ### 7. Product Versions
273
+
274
+ ```typescript
275
+ // One-time product version history
276
+ const versions = await client.graphql.query({
277
+ query: `query ($productId: String!) {
278
+ onetimeProductVersions(productId: $productId) {
279
+ id versionNumber name description
280
+ prices { currency priceInfo { amount taxCategory } }
281
+ media { type url alt thumbnail }
282
+ metadata isTestVersion isProdVersion createdAt
283
+ }
284
+ }`,
285
+ variables: { productId: "PROD_xxx" },
286
+ });
287
+
288
+ // Subscription product versions
289
+ const subVersions = await client.graphql.query({
290
+ query: `query ($productId: String!) {
291
+ subscriptionProductVersions(productId: $productId) {
292
+ id versionNumber name description billingPeriod
293
+ prices { currency priceInfo { amount taxCategory } }
294
+ metadata isTestVersion isProdVersion createdAt
295
+ }
296
+ }`,
297
+ variables: { productId: "PROD_xxx" },
298
+ });
299
+ ```
300
+
301
+ ### 8. Subscription Product Groups
302
+
303
+ ```typescript
304
+ const groups = await client.graphql.query({
305
+ query: `query ($storeId: String!) {
306
+ subscriptionProductGroups(storeId: $storeId) {
307
+ id name description
308
+ rules { sharedTrial }
309
+ environment
310
+ products {
311
+ id name billingPeriod
312
+ prices { currency priceInfo { amount taxCategory } }
313
+ }
314
+ }
315
+ }`,
316
+ variables: { storeId: "STO_xxx" },
317
+ });
318
+ ```
319
+
320
+ ### 9. Exchange Rate Query
321
+
322
+ ```typescript
323
+ const rate = await client.graphql.query({
324
+ query: `query {
325
+ rate(fromCurrency: USD, toCurrency: EUR) {
326
+ fromCurrency toCurrency standardRate rateRefId expiryTime
327
+ }
328
+ }`,
329
+ });
330
+ ```
331
+
332
+ > `CurrencyCode` is an enum type supporting 40+ ISO 4217 currency codes (e.g. `USD`, `EUR`, `GBP`, `JPY`, `CNY`). Use introspection to get the full list.
333
+
334
+ ### 10. Webhook and Email Delivery Logs
335
+
336
+ ```typescript
337
+ // Webhook delivery logs (auto-filtered by environment)
338
+ const webhookLogs = await client.graphql.query({
339
+ query: `query ($storeId: String!) {
340
+ webhookDeliveries(storeId: $storeId, limit: 20, filter: { status: { eq: "failed" } }) {
341
+ id storeId eventType eventId
342
+ payload webhookUrl status httpStatus responseBody
343
+ attemptCount lastAttemptedAt createdAt
344
+ }
345
+ webhookDeliveriesCount(storeId: $storeId, filter: { status: { eq: "failed" } })
346
+ }`,
347
+ variables: { storeId: "STO_xxx" },
348
+ });
349
+
350
+ // Email delivery logs
351
+ const emailLogs = await client.graphql.query({
352
+ query: `query ($storeId: String!) {
353
+ emailDeliveries(storeId: $storeId, limit: 20, filter: { status: { eq: "failed" } }) {
354
+ id storeId eventType eventId
355
+ recipientType toAddress subject testMode
356
+ status attemptCount lastAttemptedAt errorMessage createdAt
357
+ }
358
+ emailDeliveriesCount(storeId: $storeId, filter: { status: { eq: "failed" } })
359
+ }`,
360
+ variables: { storeId: "STO_xxx" },
361
+ });
362
+ ```
363
+
364
+ ### 11. Dashboard Overview (Combined Query)
365
+
366
+ Combine multiple queries in a single request to reduce network round trips.
367
+
368
+ ```typescript
369
+ interface DashboardQuery {
370
+ store: { name: string; slug: string; status: string } | null;
371
+ onetimeOrdersCount: number;
372
+ subscriptionOrdersCount: number;
373
+ onetimeOrders: Array<{
374
+ id: string;
375
+ buyerEmail: string;
376
+ priceSnapshot: { currency: string; total: string };
377
+ createdAt: string;
378
+ }>;
379
+ refundTickets: Array<{
380
+ id: string;
381
+ requestedAmountDetails: { currency: string; amount: string };
382
+ reason: string;
383
+ createdAt: string;
384
+ }>;
385
+ onetimeProductsCount: number;
386
+ subscriptionProductsCount: number;
387
+ }
388
+ const dashboard = await client.graphql.query<DashboardQuery>({
389
+ query: `query Dashboard($storeId: String!) {
390
+ store(id: $storeId) { name slug status }
391
+ onetimeOrdersCount(storeId: $storeId)
392
+ subscriptionOrdersCount(storeId: $storeId)
393
+ onetimeOrders(storeId: $storeId, limit: 5, filter: { status: { eq: "pending" } }) {
394
+ id buyerEmail priceSnapshot { currency total } createdAt
395
+ }
396
+ refundTickets(limit: 5, filter: { status: { eq: "pending" } }) {
397
+ id requestedAmountDetails { currency amount } reason createdAt
398
+ }
399
+ onetimeProductsCount(storeId: $storeId)
400
+ subscriptionProductsCount(storeId: $storeId)
401
+ }`,
402
+ variables: { storeId: "STO_xxx" },
403
+ });
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Count Queries
409
+
410
+ All list queries have corresponding `*Count` queries that return the total matching a filter — useful for pagination.
411
+
412
+ ```typescript
413
+ const counts = await client.graphql.query({
414
+ query: `query ($storeId: String!) {
415
+ storesCount
416
+ storeMerchantsCount
417
+ apiKeysCount
418
+ onetimeProductsCount(storeId: $storeId, filter: { status: { eq: "active" } })
419
+ subscriptionProductsCount(storeId: $storeId)
420
+ subscriptionProductGroupsCount(storeId: $storeId)
421
+ onetimeOrdersCount(storeId: $storeId)
422
+ subscriptionOrdersCount(storeId: $storeId)
423
+ paymentsCount
424
+ refundsCount
425
+ refundTicketsCount
426
+ webhookDeliveriesCount(storeId: $storeId)
427
+ emailDeliveriesCount(storeId: $storeId)
428
+ }`,
429
+ variables: { storeId: "STO_xxx" },
430
+ });
431
+ ```
432
+
433
+ > Count queries accept the same `filter` parameter as their corresponding list queries.
434
+
435
+ ---
436
+
437
+ ## Filter Types
438
+
439
+ | Filter Type | Operations | Example Fields |
440
+ |-------------|------------|----------------|
441
+ | `StringFilter` | `eq`, `ne`, `contains`, `startsWith`, `endsWith`, `in` | `status`, `name`, `email`, `currency` |
442
+ | `DateTimeFilter` | `eq`, `ne`, `gt`, `gte`, `lt`, `lte` | `createdAt`, `updatedAt`, `expiresAt` |
443
+ | `IntFilter` | `eq`, `ne`, `gt`, `gte`, `lt`, `lte` | `amount`, `totalAmount` |
444
+ | `BooleanFilter` | `eq` | `prodEnabled`, `testMode` |
445
+
446
+ > To see which filter fields are available for a specific entity, use introspection:
447
+ > `__type(name: "OnetimeOrderFilter") { fields { name type { name } } }`
448
+
449
+ ---
450
+
451
+ ## Analytics Queries
452
+
453
+ Analytics queries provide aggregated statistics, trends, and insights. All analytics queries accept `storeId` (or `storeSlug`) and an `AnalyticsFilterInput` parameter.
454
+
455
+ ### AnalyticsFilterInput
456
+
457
+ | Field | Type | Required | Description |
458
+ |-------|------|----------|-------------|
459
+ | `filter.timeRange.startDate` | `String` | Yes | Start time (ISO 8601) |
460
+ | `filter.timeRange.endDate` | `String` | Yes | End time (ISO 8601) |
461
+ | `filter.currency` | `String` | No | Currency filter (ISO 4217) |
462
+ | `filter.status` | `String` | No | Status filter |
463
+
464
+ ### TimePeriodGranularity
465
+
466
+ `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR`, `ALL_TIME`
467
+
468
+ ### orderStatistics — Order Aggregation
469
+
470
+ ```typescript
471
+ const orderStats = await client.graphql.query({
472
+ query: `query ($storeId: String!) {
473
+ orderStatistics(
474
+ storeId: $storeId,
475
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
476
+ ) {
477
+ totalCount
478
+ countsByStatus { status count }
479
+ countsByPeriod(granularity: MONTH) { period count }
480
+ revenueByCurrency { currency totalAmount paymentCount }
481
+ revenueByPeriod(granularity: MONTH, currency: "usd") { period currency totalAmount paymentCount }
482
+ buyerMetrics { totalBuyers newBuyers returningBuyers }
483
+ revenueByCountry(currency: "usd") { country totalAmount paymentCount }
484
+ ordersByCountry { country count }
485
+ b2bVsB2cBreakdown(currency: "usd") { isBusiness label totalAmount orderCount }
486
+ revenueByState(country: "US", currency: "usd") { state totalAmount paymentCount }
487
+ }
488
+ }`,
489
+ variables: { storeId: "STO_xxx" },
490
+ });
491
+ ```
492
+
493
+ ### paymentStatistics — Payment Success Rates & Refunds
494
+
495
+ ```typescript
496
+ const paymentStats = await client.graphql.query({
497
+ query: `query ($storeId: String!) {
498
+ paymentStatistics(
499
+ storeId: $storeId,
500
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
501
+ ) {
502
+ successRate { totalAttempts succeeded failed pending successRate }
503
+ failedReasons { reason count percentage }
504
+ refunds { totalCount succeededCount pendingCount failedCount amountByCurrency { currency totalAmount paymentCount } refundRate }
505
+ methodDistribution { methodType count totalAmount percentage }
506
+ cardBrandDistribution { brand count totalAmount percentage }
507
+ taxSummary { currency totalTax totalPreTax totalAmount paymentCount }
508
+ preTaxRevenueByCurrency { currency totalAmount paymentCount }
509
+ settlementRevenueByCurrency { currency totalAmount paymentCount }
510
+ }
511
+ }`,
512
+ variables: { storeId: "STO_xxx" },
513
+ });
514
+ ```
515
+
516
+ ### productStatistics — Product Rankings & Revenue
517
+
518
+ ```typescript
519
+ const productStats = await client.graphql.query({
520
+ query: `query ($storeId: String!) {
521
+ productStatistics(
522
+ storeId: $storeId,
523
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
524
+ ) {
525
+ onetimeCountsByStatus { status count }
526
+ subscriptionCountsByStatus { status count }
527
+ onetimeTotalCount
528
+ subscriptionTotalCount
529
+ topByOrderCount(limit: 10) { productId productType productName orderCount totalRevenue currency }
530
+ topByRevenue(limit: 10, currency: "usd") { productId productType productName orderCount totalRevenue currency }
531
+ revenueContribution(currency: "usd") { productId productType productName revenue contributionPercentage cumulativePercentage }
532
+ }
533
+ }`,
534
+ variables: { storeId: "STO_xxx" },
535
+ });
536
+ ```
537
+
538
+ ### trendAnalysis — Growth Trends
539
+
540
+ ```typescript
541
+ const trends = await client.graphql.query({
542
+ query: `query ($storeId: String!) {
543
+ trendAnalysis(
544
+ storeId: $storeId,
545
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
546
+ ) {
547
+ orderGrowth(granularity: MONTH) { period currentValue previousValue growthRate }
548
+ revenueGrowth(granularity: MONTH, currency: "usd") { period currentValue previousValue growthRate }
549
+ cumulativeRevenue(granularity: MONTH, currency: "usd") { period periodValue cumulativeValue }
550
+ orderMovingAverage(windowDays: 7) { date dailyValue movingAverage }
551
+ revenueMovingAverage(windowDays: 7, currency: "usd") { date dailyValue movingAverage }
552
+ }
553
+ }`,
554
+ variables: { storeId: "STO_xxx" },
555
+ });
556
+ ```
557
+
558
+ ### distributionAnalysis — Amount Distribution & AOV
559
+
560
+ ```typescript
561
+ const distribution = await client.graphql.query({
562
+ query: `query ($storeId: String!) {
563
+ distributionAnalysis(
564
+ storeId: $storeId,
565
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
566
+ ) {
567
+ orderAmountPercentiles(currency: "usd") { p10 p25 p50 p75 p90 p95 p99 min max avg stddev count }
568
+ aovTrend(granularity: MONTH, currency: "usd") { period averageOrderValue orderCount totalRevenue }
569
+ orderAmountBuckets(currency: "usd", bucketCount: 10) { rangeMin rangeMax count percentage }
570
+ }
571
+ }`,
572
+ variables: { storeId: "STO_xxx" },
573
+ });
574
+ ```
575
+
576
+ ### customerAnalysis — Retention, LTV & Repeat Purchases
577
+
578
+ ```typescript
579
+ const customers = await client.graphql.query({
580
+ query: `query ($storeId: String!) {
581
+ customerAnalysis(
582
+ storeId: $storeId,
583
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
584
+ ) {
585
+ cohortRetention(granularity: MONTH) {
586
+ cohortPeriod cohortSize
587
+ retention { periodOffset activeCustomers retentionRate }
588
+ }
589
+ ltvDistribution(currency: "usd") {
590
+ averageLtv medianLtv
591
+ buckets { rangeMin rangeMax count percentage }
592
+ }
593
+ purchaseFrequency { purchaseCount customerCount percentage }
594
+ repeatPurchaseRate(granularity: MONTH) { period totalBuyers repeatBuyers repeatRate }
595
+ topCustomers(limit: 10, currency: "usd") { buyerEmail totalSpent orderCount firstPurchaseDate lastPurchaseDate }
596
+ }
597
+ }`,
598
+ variables: { storeId: "STO_xxx" },
599
+ });
600
+ ```
601
+
602
+ ### taxAnalysis — Tax Breakdown
603
+
604
+ ```typescript
605
+ const tax = await client.graphql.query({
606
+ query: `query ($storeId: String!) {
607
+ taxAnalysis(
608
+ storeId: $storeId,
609
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
610
+ ) {
611
+ byCategory(currency: "usd") { taxCategory totalTax totalAmount paymentCount }
612
+ byRateGroup(currency: "usd") { taxRate totalTax totalAmount paymentCount }
613
+ byCountry(currency: "usd") { country totalTax totalAmount paymentCount }
614
+ b2bVsB2c(currency: "usd") { isBusiness label totalTax totalAmount orderCount }
615
+ effectiveTaxRateTrend(granularity: MONTH, currency: "usd") { period avgTaxRate paymentCount }
616
+ taxAmountByPeriod(granularity: MONTH, currency: "usd") { period totalTax paymentCount }
617
+ }
618
+ }`,
619
+ variables: { storeId: "STO_xxx" },
620
+ });
621
+ ```
622
+
623
+ ### subscriptionAnalysis — Churn, Trial Conversion & Billing
624
+
625
+ ```typescript
626
+ const subscriptions = await client.graphql.query({
627
+ query: `query ($storeId: String!) {
628
+ subscriptionAnalysis(
629
+ storeId: $storeId,
630
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
631
+ ) {
632
+ billingPeriodDistribution(currency: "usd") { billingPeriod count totalAmount percentage }
633
+ activeCount
634
+ cancellationStats { totalSubscriptions canceledCount cancellationRate avgLifetimeDays medianLifetimeDays }
635
+ cancellationTrend(granularity: MONTH) { period canceledCount }
636
+ trialConversion { totalTrials convertedCount activeTrials conversionRate }
637
+ trialConversionByProduct { productId productName totalTrials convertedCount conversionRate }
638
+ churnRate(granularity: MONTH) { period startActive churned churnRate }
639
+ }
640
+ }`,
641
+ variables: { storeId: "STO_xxx" },
642
+ });
643
+ ```
644
+
645
+ ### refundTicketAnalysis — Refund Reasons & Review Efficiency
646
+
647
+ ```typescript
648
+ const refundAnalysis = await client.graphql.query({
649
+ query: `query ($storeId: String!) {
650
+ refundTicketAnalysis(
651
+ storeId: $storeId,
652
+ filter: { timeRange: { startDate: "2025-01-01T00:00:00Z", endDate: "2026-01-01T00:00:00Z" } }
653
+ ) {
654
+ reasonDistribution(currency: "usd") { reason count totalAmount percentage }
655
+ statusDistribution { status count percentage }
656
+ reviewEfficiency { avgHours medianHours p90Hours totalReviewed }
657
+ ticketTrend(granularity: MONTH) { period totalCreated resolvedCount approvedCount rejectedCount }
658
+ approvalRate { approved rejected rate }
659
+ processingSuccessRate { succeeded failed rate }
660
+ }
661
+ }`,
662
+ variables: { storeId: "STO_xxx" },
663
+ });
664
+ ```