@onesub/server 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/__tests__/google-subscriptions-v2.test.d.ts +1 -1
  2. package/dist/__tests__/receipt-age.test.d.ts +7 -0
  3. package/dist/__tests__/receipt-age.test.d.ts.map +1 -0
  4. package/dist/__tests__/tracing.test.d.ts +2 -0
  5. package/dist/__tests__/tracing.test.d.ts.map +1 -0
  6. package/dist/cache.d.ts +5 -0
  7. package/dist/cache.d.ts.map +1 -1
  8. package/dist/index.cjs +178 -55
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.js +178 -55
  11. package/dist/index.js.map +1 -1
  12. package/dist/providers/apple.d.ts.map +1 -1
  13. package/dist/providers/google.d.ts.map +1 -1
  14. package/dist/providers/mock.d.ts.map +1 -1
  15. package/dist/routes/admin.d.ts.map +1 -1
  16. package/dist/routes/purchase.d.ts.map +1 -1
  17. package/dist/routes/validate.d.ts.map +1 -1
  18. package/dist/routes/webhook-apple.d.ts.map +1 -1
  19. package/dist/routes/webhook-google.d.ts +1 -1
  20. package/dist/routes/webhook-google.d.ts.map +1 -1
  21. package/dist/store.d.ts.map +1 -1
  22. package/dist/stores/postgres.d.ts.map +1 -1
  23. package/dist/stores/redis.d.ts +1 -0
  24. package/dist/stores/redis.d.ts.map +1 -1
  25. package/dist/tracing.d.ts.map +1 -1
  26. package/dist/webhook-events.d.ts +10 -0
  27. package/dist/webhook-events.d.ts.map +1 -1
  28. package/package.json +5 -3
  29. package/dist/__tests__/admin-customer-detail.test.js +0 -146
  30. package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
  31. package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
  32. package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
  33. package/dist/__tests__/admin-subscription-detail.test.js +0 -112
  34. package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
  35. package/dist/__tests__/apple-root-ca.test.js +0 -41
  36. package/dist/__tests__/apple-root-ca.test.js.map +0 -1
  37. package/dist/__tests__/apple-status-api.test.js +0 -345
  38. package/dist/__tests__/apple-status-api.test.js.map +0 -1
  39. package/dist/__tests__/cache.test.js +0 -34
  40. package/dist/__tests__/cache.test.js.map +0 -1
  41. package/dist/__tests__/entitlements.test.js +0 -268
  42. package/dist/__tests__/entitlements.test.js.map +0 -1
  43. package/dist/__tests__/error-codes.test.js +0 -173
  44. package/dist/__tests__/error-codes.test.js.map +0 -1
  45. package/dist/__tests__/fetch-hardening.test.js +0 -192
  46. package/dist/__tests__/fetch-hardening.test.js.map +0 -1
  47. package/dist/__tests__/google-paused.test.js +0 -126
  48. package/dist/__tests__/google-paused.test.js.map +0 -1
  49. package/dist/__tests__/google-price-change.test.js +0 -186
  50. package/dist/__tests__/google-price-change.test.js.map +0 -1
  51. package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
  52. package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
  53. package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
  54. package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
  55. package/dist/__tests__/lifecycle-states.test.js +0 -423
  56. package/dist/__tests__/lifecycle-states.test.js.map +0 -1
  57. package/dist/__tests__/linked-purchase-token.test.js +0 -227
  58. package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
  59. package/dist/__tests__/metrics.test.js +0 -340
  60. package/dist/__tests__/metrics.test.js.map +0 -1
  61. package/dist/__tests__/mock-provider.test.js +0 -188
  62. package/dist/__tests__/mock-provider.test.js.map +0 -1
  63. package/dist/__tests__/openapi.test.js +0 -32
  64. package/dist/__tests__/openapi.test.js.map +0 -1
  65. package/dist/__tests__/paused-auto-resume.test.js +0 -162
  66. package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
  67. package/dist/__tests__/providers.test.js +0 -321
  68. package/dist/__tests__/providers.test.js.map +0 -1
  69. package/dist/__tests__/redis-store.test.js +0 -166
  70. package/dist/__tests__/redis-store.test.js.map +0 -1
  71. package/dist/__tests__/refund-policy.test.js +0 -317
  72. package/dist/__tests__/refund-policy.test.js.map +0 -1
  73. package/dist/__tests__/schema.test.js +0 -46
  74. package/dist/__tests__/schema.test.js.map +0 -1
  75. package/dist/__tests__/store.test.js +0 -172
  76. package/dist/__tests__/store.test.js.map +0 -1
  77. package/dist/__tests__/test-utils.js +0 -30
  78. package/dist/__tests__/test-utils.js.map +0 -1
  79. package/dist/__tests__/webhook-apple.test.js +0 -247
  80. package/dist/__tests__/webhook-apple.test.js.map +0 -1
  81. package/dist/__tests__/webhook-events.test.js +0 -36
  82. package/dist/__tests__/webhook-events.test.js.map +0 -1
  83. package/dist/__tests__/webhook-google.test.js +0 -255
  84. package/dist/__tests__/webhook-google.test.js.map +0 -1
  85. package/dist/__tests__/webhook-queue.test.js +0 -26
  86. package/dist/__tests__/webhook-queue.test.js.map +0 -1
  87. package/dist/__tests__/webhook-refund.test.js +0 -313
  88. package/dist/__tests__/webhook-refund.test.js.map +0 -1
  89. package/dist/cache.js +0 -39
  90. package/dist/cache.js.map +0 -1
  91. package/dist/errors.js +0 -19
  92. package/dist/errors.js.map +0 -1
  93. package/dist/http.js +0 -36
  94. package/dist/http.js.map +0 -1
  95. package/dist/logger.js +0 -20
  96. package/dist/logger.js.map +0 -1
  97. package/dist/openapi.js +0 -196
  98. package/dist/openapi.js.map +0 -1
  99. package/dist/providers/apple-root-ca.js +0 -38
  100. package/dist/providers/apple-root-ca.js.map +0 -1
  101. package/dist/providers/apple.js +0 -630
  102. package/dist/providers/apple.js.map +0 -1
  103. package/dist/providers/google.js +0 -564
  104. package/dist/providers/google.js.map +0 -1
  105. package/dist/providers/mock.js +0 -93
  106. package/dist/providers/mock.js.map +0 -1
  107. package/dist/routes/admin.js +0 -327
  108. package/dist/routes/admin.js.map +0 -1
  109. package/dist/routes/apple-offer.js +0 -72
  110. package/dist/routes/apple-offer.js.map +0 -1
  111. package/dist/routes/entitlements.js +0 -130
  112. package/dist/routes/entitlements.js.map +0 -1
  113. package/dist/routes/metrics.js +0 -281
  114. package/dist/routes/metrics.js.map +0 -1
  115. package/dist/routes/purchase.js +0 -204
  116. package/dist/routes/purchase.js.map +0 -1
  117. package/dist/routes/status.js +0 -55
  118. package/dist/routes/status.js.map +0 -1
  119. package/dist/routes/validate.js +0 -70
  120. package/dist/routes/validate.js.map +0 -1
  121. package/dist/routes/webhook-apple.js +0 -145
  122. package/dist/routes/webhook-apple.js.map +0 -1
  123. package/dist/routes/webhook-google.js +0 -204
  124. package/dist/routes/webhook-google.js.map +0 -1
  125. package/dist/routes/webhook.js +0 -19
  126. package/dist/routes/webhook.js.map +0 -1
  127. package/dist/store.js +0 -145
  128. package/dist/store.js.map +0 -1
  129. package/dist/stores/postgres.js +0 -329
  130. package/dist/stores/postgres.js.map +0 -1
  131. package/dist/stores/redis.js +0 -297
  132. package/dist/stores/redis.js.map +0 -1
  133. package/dist/stores/schema.js +0 -67
  134. package/dist/stores/schema.js.map +0 -1
  135. package/dist/tracing.js +0 -74
  136. package/dist/tracing.js.map +0 -1
  137. package/dist/webhook-events.js +0 -58
  138. package/dist/webhook-events.js.map +0 -1
  139. package/dist/webhook-queue.js +0 -108
  140. package/dist/webhook-queue.js.map +0 -1
@@ -1,188 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import request from 'supertest';
3
- import express from 'express';
4
- import { ONESUB_ERROR_CODE, SUBSCRIPTION_STATUS, MOCK_RECEIPT_PREFIX } from '@onesub/shared';
5
- import { createOneSubMiddleware } from '../index.js';
6
- import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
7
- import { classifyMockReceipt, mockValidateAppleSubscription, mockValidateAppleProduct, mockValidateGoogleSubscription, mockValidateGoogleProduct, } from '../providers/mock.js';
8
- // ---------------------------------------------------------------------------
9
- // Pure classifier
10
- // ---------------------------------------------------------------------------
11
- describe('classifyMockReceipt', () => {
12
- const cases = [
13
- [`${MOCK_RECEIPT_PREFIX.REVOKED}_tx1`, 'revoked'],
14
- [`${MOCK_RECEIPT_PREFIX.EXPIRED}_tx1`, 'expired'],
15
- [`${MOCK_RECEIPT_PREFIX.INVALID}_tx1`, 'invalid'],
16
- [`${MOCK_RECEIPT_PREFIX.BAD_SIG}_tx1`, 'invalid'],
17
- [`${MOCK_RECEIPT_PREFIX.NETWORK_ERROR}_tx1`, 'network-error'],
18
- [`${MOCK_RECEIPT_PREFIX.SANDBOX}_tx1`, 'sandbox'],
19
- ['MOCK_anything_else', 'valid'],
20
- ['not_even_a_mock_prefix', 'valid'],
21
- ];
22
- for (const [receipt, expected] of cases) {
23
- it(`classifies ${receipt} as ${expected}`, () => {
24
- expect(classifyMockReceipt(receipt).kind).toBe(expected);
25
- });
26
- }
27
- });
28
- // ---------------------------------------------------------------------------
29
- // Pure mock provider functions
30
- // ---------------------------------------------------------------------------
31
- describe('mockValidateAppleSubscription', () => {
32
- it('returns ACTIVE subscription for valid receipt', () => {
33
- const sub = mockValidateAppleSubscription('MOCK_VALID_sub_1');
34
- expect(sub).toMatchObject({
35
- platform: 'apple',
36
- status: SUBSCRIPTION_STATUS.ACTIVE,
37
- willRenew: true,
38
- });
39
- expect(sub?.originalTransactionId).toContain('mock_apple_orig_');
40
- });
41
- it('returns null for MOCK_REVOKED', () => {
42
- expect(mockValidateAppleSubscription('MOCK_REVOKED_x')).toBeNull();
43
- });
44
- it('throws for MOCK_NETWORK_ERROR', () => {
45
- expect(() => mockValidateAppleSubscription('MOCK_NETWORK_ERROR_x')).toThrow(/network error/);
46
- });
47
- it('produces deterministic transactionIds', () => {
48
- const a = mockValidateAppleSubscription('MOCK_VALID_same');
49
- const b = mockValidateAppleSubscription('MOCK_VALID_same');
50
- expect(a?.originalTransactionId).toBe(b?.originalTransactionId);
51
- });
52
- it('sandbox receipts get shorter expiry (~1h)', () => {
53
- const sub = mockValidateAppleSubscription('MOCK_SANDBOX_x');
54
- const expiry = Date.parse(sub.expiresAt);
55
- const ageMs = expiry - Date.now();
56
- expect(ageMs).toBeGreaterThan(30 * 60 * 1000); // > 30 min
57
- expect(ageMs).toBeLessThan(2 * 60 * 60 * 1000); // < 2 h
58
- });
59
- });
60
- describe('mockValidateAppleProduct', () => {
61
- it('returns product result with productId for valid receipt', () => {
62
- const result = mockValidateAppleProduct('MOCK_VALID_x', 'premium');
63
- expect(result).toMatchObject({ productId: 'premium' });
64
- expect(result?.transactionId).toContain('mock_apple_premium_');
65
- });
66
- it('returns null for MOCK_EXPIRED', () => {
67
- expect(mockValidateAppleProduct('MOCK_EXPIRED_x', 'premium')).toBeNull();
68
- });
69
- });
70
- describe('mockValidateGoogleSubscription', () => {
71
- it('returns ACTIVE subscription for valid token', () => {
72
- const sub = mockValidateGoogleSubscription('MOCK_VALID_x', 'pro_monthly');
73
- expect(sub).toMatchObject({
74
- platform: 'google',
75
- productId: 'pro_monthly',
76
- status: SUBSCRIPTION_STATUS.ACTIVE,
77
- });
78
- });
79
- it('throws for MOCK_NETWORK_ERROR', () => {
80
- expect(() => mockValidateGoogleSubscription('MOCK_NETWORK_ERROR_x', 'pro'))
81
- .toThrow(/network error/);
82
- });
83
- });
84
- describe('mockValidateGoogleProduct', () => {
85
- it('returns product result for valid token', () => {
86
- const result = mockValidateGoogleProduct('MOCK_VALID_x', 'credits_100');
87
- expect(result?.transactionId).toContain('mock_google_credits_100_');
88
- });
89
- it('returns null for MOCK_INVALID', () => {
90
- expect(mockValidateGoogleProduct('MOCK_INVALID_x', 'credits_100')).toBeNull();
91
- });
92
- });
93
- // ---------------------------------------------------------------------------
94
- // HTTP integration — full onesub server with mockMode on both platforms.
95
- // This is the key demo: a server standing up without real Apple / Google
96
- // credentials, yet the full purchase flow completes end-to-end.
97
- // ---------------------------------------------------------------------------
98
- describe('full server in mockMode', () => {
99
- function mockApp() {
100
- const config = {
101
- database: { url: '' },
102
- apple: { bundleId: 'com.test.mock', mockMode: true },
103
- google: { packageName: 'com.test.mock', mockMode: true },
104
- };
105
- const app = express();
106
- app.use(createOneSubMiddleware({
107
- ...config,
108
- store: new InMemorySubscriptionStore(),
109
- purchaseStore: new InMemoryPurchaseStore(),
110
- }));
111
- return app;
112
- }
113
- it('validates an Apple subscription without any real credentials', async () => {
114
- const app = mockApp();
115
- const res = await request(app).post('/onesub/validate').send({
116
- platform: 'apple',
117
- receipt: 'MOCK_VALID_apple_sub_1',
118
- userId: 'user_1',
119
- productId: 'pro_monthly',
120
- });
121
- expect(res.status).toBe(200);
122
- expect(res.body.valid).toBe(true);
123
- expect(res.body.subscription.status).toBe('active');
124
- });
125
- it('validates a Google non-consumable purchase without credentials', async () => {
126
- const app = mockApp();
127
- const res = await request(app).post('/onesub/purchase/validate').send({
128
- platform: 'google',
129
- receipt: 'MOCK_VALID_google_product_1',
130
- userId: 'user_1',
131
- productId: 'premium',
132
- type: 'non_consumable',
133
- });
134
- expect(res.status).toBe(200);
135
- expect(res.body.valid).toBe(true);
136
- expect(res.body.action).toBe('new');
137
- });
138
- it('MOCK_REVOKED returns RECEIPT_VALIDATION_FAILED via normal error path', async () => {
139
- const app = mockApp();
140
- const res = await request(app).post('/onesub/purchase/validate').send({
141
- platform: 'apple',
142
- receipt: 'MOCK_REVOKED_x',
143
- userId: 'user_1',
144
- productId: 'premium',
145
- type: 'non_consumable',
146
- });
147
- expect(res.status).toBe(422);
148
- expect(res.body.errorCode).toBe(ONESUB_ERROR_CODE.RECEIPT_VALIDATION_FAILED);
149
- });
150
- it('throws when NODE_ENV=production + mockMode is set (fraud guard)', () => {
151
- const original = process.env['NODE_ENV'];
152
- process.env['NODE_ENV'] = 'production';
153
- try {
154
- expect(() => createOneSubMiddleware({
155
- database: { url: '' },
156
- apple: { bundleId: 'com.test', mockMode: true },
157
- store: new InMemorySubscriptionStore(),
158
- purchaseStore: new InMemoryPurchaseStore(),
159
- })).toThrow(/mockMode cannot be enabled when NODE_ENV=production/);
160
- }
161
- finally {
162
- if (original === undefined)
163
- delete process.env['NODE_ENV'];
164
- else
165
- process.env['NODE_ENV'] = original;
166
- }
167
- });
168
- it('replaying the same receipt returns action:restored (server idempotency)', async () => {
169
- const app = mockApp();
170
- const body = {
171
- platform: 'apple',
172
- receipt: 'MOCK_VALID_replay_test',
173
- userId: 'user_1',
174
- productId: 'premium',
175
- type: 'non_consumable',
176
- };
177
- const first = await request(app).post('/onesub/purchase/validate').send(body);
178
- expect(first.body.action).toBe('new');
179
- // Wipe the user's record so hasPurchased() is false (NON_CONSUMABLE_ALREADY_OWNED
180
- // would short-circuit otherwise). Use a different userId to hit the same
181
- // transactionId path.
182
- const second = await request(app).post('/onesub/purchase/validate').send({ ...body, userId: 'user_2' });
183
- // non-consumable with same transactionId but different userId → auto-reassigned (0.6.1+)
184
- expect(second.body.valid).toBe(true);
185
- expect(second.body.action).toBe('restored');
186
- });
187
- });
188
- //# sourceMappingURL=mock-provider.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mock-provider.test.js","sourceRoot":"","sources":["../../src/__tests__/mock-provider.test.ts"],"names":[],"mappings":"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"}
@@ -1,32 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { ONESUB_OPENAPI } from '../openapi.js';
3
- import { ROUTES } from '@onesub/shared';
4
- describe('OpenAPI document', () => {
5
- it('declares every public route', () => {
6
- const declared = Object.keys(ONESUB_OPENAPI.paths);
7
- // Spot-check the main public routes — the spec is hand-written, so this
8
- // is the parity guard. Routes added to ROUTES that aren't in the spec
9
- // should fail this test.
10
- for (const path of [
11
- ROUTES.VALIDATE,
12
- ROUTES.STATUS,
13
- ROUTES.WEBHOOK_APPLE,
14
- ROUTES.WEBHOOK_GOOGLE,
15
- ROUTES.ENTITLEMENTS,
16
- ROUTES.ADMIN_SUBSCRIPTIONS,
17
- ]) {
18
- expect(declared).toContain(path);
19
- }
20
- });
21
- it('uses 3.1.0', () => {
22
- expect(ONESUB_OPENAPI.openapi).toBe('3.1.0');
23
- });
24
- it('declares the AdminSecret security scheme', () => {
25
- expect(ONESUB_OPENAPI.components.securitySchemes.AdminSecret).toMatchObject({
26
- type: 'apiKey',
27
- in: 'header',
28
- name: 'X-Admin-Secret',
29
- });
30
- });
31
- });
32
- //# sourceMappingURL=openapi.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openapi.test.js","sourceRoot":"","sources":["../../src/__tests__/openapi.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACnD,wEAAwE;QACxE,sEAAsE;QACtE,yBAAyB;QACzB,KAAK,MAAM,IAAI,IAAI;YACjB,MAAM,CAAC,QAAQ;YACf,MAAM,CAAC,MAAM;YACb,MAAM,CAAC,aAAa;YACpB,MAAM,CAAC,cAAc;YACrB,MAAM,CAAC,YAAY;YACnB,MAAM,CAAC,mBAAmB;SAC3B,EAAE,CAAC;YACF,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;QACpB,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,aAAa,CAAC;YAC1E,IAAI,EAAE,QAAQ;YACd,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,gBAAgB;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,162 +0,0 @@
1
- /**
2
- * Tests for SubscriptionInfo.autoResumeTime — Google paused subscriptions
3
- * surface the auto-resume timestamp from v2 pausedStateContext, so host apps
4
- * can render "재개 예정: YYYY-MM-DD" UX.
5
- */
6
- import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest';
7
- import express from 'express';
8
- import { generateKeyPairSync } from 'crypto';
9
- import { SUBSCRIPTION_STATUS } from '@onesub/shared';
10
- import { validateGoogleReceipt } from '../providers/google.js';
11
- import { createStatusRouter } from '../routes/status.js';
12
- import { InMemorySubscriptionStore } from '../store.js';
13
- import { isLocalhostUrl, urlHost } from './test-utils.js';
14
- let testPrivateKey;
15
- beforeAll(() => {
16
- const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
17
- testPrivateKey = privateKey.export({ type: 'pkcs8', format: 'pem' });
18
- });
19
- beforeEach(() => {
20
- vi.restoreAllMocks();
21
- });
22
- function googleConfig() {
23
- return {
24
- packageName: 'com.example.app',
25
- serviceAccountKey: JSON.stringify({
26
- client_email: `test-${Math.random()}@test.iam.gserviceaccount.com`,
27
- private_key: testPrivateKey,
28
- token_uri: 'https://oauth2.googleapis.com/token',
29
- }),
30
- };
31
- }
32
- function v2Paused(opts) {
33
- return {
34
- startTime: '2026-01-01T00:00:00Z',
35
- subscriptionState: 'SUBSCRIPTION_STATE_PAUSED',
36
- latestOrderId: 'GPA.paused_order',
37
- pausedStateContext: opts.autoResumeTime ? { autoResumeTime: opts.autoResumeTime } : undefined,
38
- lineItems: [{
39
- productId: opts.productId ?? 'pro_monthly',
40
- expiryTime: '2027-01-01T00:00:00Z',
41
- autoRenewingPlan: { autoRenewEnabled: true },
42
- }],
43
- };
44
- }
45
- function v2Active(productId = 'pro_monthly') {
46
- return {
47
- startTime: '2026-01-01T00:00:00Z',
48
- subscriptionState: 'SUBSCRIPTION_STATE_ACTIVE',
49
- latestOrderId: 'GPA.active_order',
50
- lineItems: [{
51
- productId,
52
- expiryTime: '2027-01-01T00:00:00Z',
53
- autoRenewingPlan: { autoRenewEnabled: true },
54
- }],
55
- };
56
- }
57
- function mockV2Fetch(responseBody) {
58
- const originalFetch = global.fetch;
59
- vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => {
60
- if (isLocalhostUrl(url))
61
- return originalFetch(url, init);
62
- const host = urlHost(url);
63
- if (host === 'oauth2.googleapis.com') {
64
- return {
65
- ok: true,
66
- json: async () => ({ access_token: 'tok', expires_in: 3600 }),
67
- text: async () => '',
68
- };
69
- }
70
- if (host === 'androidpublisher.googleapis.com') {
71
- return {
72
- ok: true,
73
- json: async () => responseBody,
74
- text: async () => JSON.stringify(responseBody),
75
- };
76
- }
77
- throw new Error(`[test] Unexpected URL: ${String(url)}`);
78
- });
79
- }
80
- // ── validateGoogleReceipt surfaces autoResumeTime ───────────────────────────
81
- describe('validateGoogleReceipt — autoResumeTime passthrough', () => {
82
- it('surfaces autoResumeTime when subscription is paused', async () => {
83
- mockV2Fetch(v2Paused({ autoResumeTime: '2026-08-15T00:00:00Z' }));
84
- const result = await validateGoogleReceipt('tok_paused', 'pro_monthly', googleConfig());
85
- expect(result?.status).toBe(SUBSCRIPTION_STATUS.PAUSED);
86
- expect(result?.autoResumeTime).toBe('2026-08-15T00:00:00Z');
87
- });
88
- it('autoResumeTime is undefined when paused but Google omits the timestamp', async () => {
89
- // Google sometimes returns SUBSCRIPTION_STATE_PAUSED without
90
- // pausedStateContext (rare, but the API allows it).
91
- mockV2Fetch(v2Paused({}));
92
- const result = await validateGoogleReceipt('tok_paused_no_ctx', 'pro_monthly', googleConfig());
93
- expect(result?.status).toBe(SUBSCRIPTION_STATUS.PAUSED);
94
- expect(result?.autoResumeTime).toBeUndefined();
95
- });
96
- it('autoResumeTime is undefined for active subscription (no pausedStateContext)', async () => {
97
- mockV2Fetch(v2Active('pro_monthly'));
98
- const result = await validateGoogleReceipt('tok_active', 'pro_monthly', googleConfig());
99
- expect(result?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
100
- expect(result?.autoResumeTime).toBeUndefined();
101
- });
102
- it('autoResumeTime is undefined even if pausedStateContext appears on a non-paused state (defensive)', async () => {
103
- // Google shouldn't send pausedStateContext for non-paused states, but if
104
- // they ever do, we should not surface it as if the user were paused.
105
- mockV2Fetch({
106
- ...v2Active('pro_monthly'),
107
- pausedStateContext: { autoResumeTime: '2099-01-01T00:00:00Z' },
108
- });
109
- const result = await validateGoogleReceipt('tok_defensive', 'pro_monthly', googleConfig());
110
- expect(result?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
111
- expect(result?.autoResumeTime).toBeUndefined();
112
- });
113
- });
114
- function spinUpStatus() {
115
- const store = new InMemorySubscriptionStore();
116
- const app = express();
117
- app.use(express.json());
118
- app.use(createStatusRouter(store));
119
- return {
120
- store,
121
- server: {
122
- async request(path) {
123
- const httpServer = app.listen(0);
124
- const address = httpServer.address();
125
- const port = typeof address === 'object' && address ? address.port : 0;
126
- try {
127
- const resp = await fetch(`http://127.0.0.1:${port}${path}`);
128
- const body = await resp.json();
129
- return { status: resp.status, body };
130
- }
131
- finally {
132
- await new Promise((resolve) => httpServer.close(() => resolve()));
133
- }
134
- },
135
- },
136
- };
137
- }
138
- const sampleSub = (overrides) => ({
139
- userId: 'u_pause',
140
- productId: 'pro_monthly',
141
- platform: 'google',
142
- status: 'paused',
143
- expiresAt: '2099-01-01T00:00:00.000Z',
144
- originalTransactionId: 'tok_p',
145
- purchasedAt: '2026-01-01T00:00:00.000Z',
146
- willRenew: true,
147
- ...overrides,
148
- });
149
- describe('status route — autoResumeTime in subscription payload', () => {
150
- it('returns the saved autoResumeTime so the host can show "재개 예정" UX', async () => {
151
- const { store, server } = spinUpStatus();
152
- await store.save(sampleSub({
153
- userId: 'u_with_resume',
154
- autoResumeTime: '2026-09-01T00:00:00.000Z',
155
- }));
156
- const resp = await server.request('/onesub/status?userId=u_with_resume');
157
- const body = resp.body;
158
- expect(body.active).toBe(false); // paused stays revoked
159
- expect(body.subscription?.autoResumeTime).toBe('2026-09-01T00:00:00.000Z');
160
- });
161
- });
162
- //# sourceMappingURL=paused-auto-resume.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paused-auto-resume.test.js","sourceRoot":"","sources":["../../src/__tests__/paused-auto-resume.test.ts"],"names":[],"mappings":"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"}