@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.
- package/dist/__tests__/google-subscriptions-v2.test.d.ts +1 -1
- package/dist/__tests__/receipt-age.test.d.ts +7 -0
- package/dist/__tests__/receipt-age.test.d.ts.map +1 -0
- package/dist/__tests__/tracing.test.d.ts +2 -0
- package/dist/__tests__/tracing.test.d.ts.map +1 -0
- package/dist/cache.d.ts +5 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/index.cjs +178 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +178 -55
- package/dist/index.js.map +1 -1
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/mock.d.ts.map +1 -1
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/validate.d.ts.map +1 -1
- package/dist/routes/webhook-apple.d.ts.map +1 -1
- package/dist/routes/webhook-google.d.ts +1 -1
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/stores/postgres.d.ts.map +1 -1
- package/dist/stores/redis.d.ts +1 -0
- package/dist/stores/redis.d.ts.map +1 -1
- package/dist/tracing.d.ts.map +1 -1
- package/dist/webhook-events.d.ts +10 -0
- package/dist/webhook-events.d.ts.map +1 -1
- package/package.json +5 -3
- package/dist/__tests__/admin-customer-detail.test.js +0 -146
- package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
- package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
- package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
- package/dist/__tests__/admin-subscription-detail.test.js +0 -112
- package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
- package/dist/__tests__/apple-root-ca.test.js +0 -41
- package/dist/__tests__/apple-root-ca.test.js.map +0 -1
- package/dist/__tests__/apple-status-api.test.js +0 -345
- package/dist/__tests__/apple-status-api.test.js.map +0 -1
- package/dist/__tests__/cache.test.js +0 -34
- package/dist/__tests__/cache.test.js.map +0 -1
- package/dist/__tests__/entitlements.test.js +0 -268
- package/dist/__tests__/entitlements.test.js.map +0 -1
- package/dist/__tests__/error-codes.test.js +0 -173
- package/dist/__tests__/error-codes.test.js.map +0 -1
- package/dist/__tests__/fetch-hardening.test.js +0 -192
- package/dist/__tests__/fetch-hardening.test.js.map +0 -1
- package/dist/__tests__/google-paused.test.js +0 -126
- package/dist/__tests__/google-paused.test.js.map +0 -1
- package/dist/__tests__/google-price-change.test.js +0 -186
- package/dist/__tests__/google-price-change.test.js.map +0 -1
- package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
- package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
- package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
- package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
- package/dist/__tests__/lifecycle-states.test.js +0 -423
- package/dist/__tests__/lifecycle-states.test.js.map +0 -1
- package/dist/__tests__/linked-purchase-token.test.js +0 -227
- package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
- package/dist/__tests__/metrics.test.js +0 -340
- package/dist/__tests__/metrics.test.js.map +0 -1
- package/dist/__tests__/mock-provider.test.js +0 -188
- package/dist/__tests__/mock-provider.test.js.map +0 -1
- package/dist/__tests__/openapi.test.js +0 -32
- package/dist/__tests__/openapi.test.js.map +0 -1
- package/dist/__tests__/paused-auto-resume.test.js +0 -162
- package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
- package/dist/__tests__/providers.test.js +0 -321
- package/dist/__tests__/providers.test.js.map +0 -1
- package/dist/__tests__/redis-store.test.js +0 -166
- package/dist/__tests__/redis-store.test.js.map +0 -1
- package/dist/__tests__/refund-policy.test.js +0 -317
- package/dist/__tests__/refund-policy.test.js.map +0 -1
- package/dist/__tests__/schema.test.js +0 -46
- package/dist/__tests__/schema.test.js.map +0 -1
- package/dist/__tests__/store.test.js +0 -172
- package/dist/__tests__/store.test.js.map +0 -1
- package/dist/__tests__/test-utils.js +0 -30
- package/dist/__tests__/test-utils.js.map +0 -1
- package/dist/__tests__/webhook-apple.test.js +0 -247
- package/dist/__tests__/webhook-apple.test.js.map +0 -1
- package/dist/__tests__/webhook-events.test.js +0 -36
- package/dist/__tests__/webhook-events.test.js.map +0 -1
- package/dist/__tests__/webhook-google.test.js +0 -255
- package/dist/__tests__/webhook-google.test.js.map +0 -1
- package/dist/__tests__/webhook-queue.test.js +0 -26
- package/dist/__tests__/webhook-queue.test.js.map +0 -1
- package/dist/__tests__/webhook-refund.test.js +0 -313
- package/dist/__tests__/webhook-refund.test.js.map +0 -1
- package/dist/cache.js +0 -39
- package/dist/cache.js.map +0 -1
- package/dist/errors.js +0 -19
- package/dist/errors.js.map +0 -1
- package/dist/http.js +0 -36
- package/dist/http.js.map +0 -1
- package/dist/logger.js +0 -20
- package/dist/logger.js.map +0 -1
- package/dist/openapi.js +0 -196
- package/dist/openapi.js.map +0 -1
- package/dist/providers/apple-root-ca.js +0 -38
- package/dist/providers/apple-root-ca.js.map +0 -1
- package/dist/providers/apple.js +0 -630
- package/dist/providers/apple.js.map +0 -1
- package/dist/providers/google.js +0 -564
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/mock.js +0 -93
- package/dist/providers/mock.js.map +0 -1
- package/dist/routes/admin.js +0 -327
- package/dist/routes/admin.js.map +0 -1
- package/dist/routes/apple-offer.js +0 -72
- package/dist/routes/apple-offer.js.map +0 -1
- package/dist/routes/entitlements.js +0 -130
- package/dist/routes/entitlements.js.map +0 -1
- package/dist/routes/metrics.js +0 -281
- package/dist/routes/metrics.js.map +0 -1
- package/dist/routes/purchase.js +0 -204
- package/dist/routes/purchase.js.map +0 -1
- package/dist/routes/status.js +0 -55
- package/dist/routes/status.js.map +0 -1
- package/dist/routes/validate.js +0 -70
- package/dist/routes/validate.js.map +0 -1
- package/dist/routes/webhook-apple.js +0 -145
- package/dist/routes/webhook-apple.js.map +0 -1
- package/dist/routes/webhook-google.js +0 -204
- package/dist/routes/webhook-google.js.map +0 -1
- package/dist/routes/webhook.js +0 -19
- package/dist/routes/webhook.js.map +0 -1
- package/dist/store.js +0 -145
- package/dist/store.js.map +0 -1
- package/dist/stores/postgres.js +0 -329
- package/dist/stores/postgres.js.map +0 -1
- package/dist/stores/redis.js +0 -297
- package/dist/stores/redis.js.map +0 -1
- package/dist/stores/schema.js +0 -67
- package/dist/stores/schema.js.map +0 -1
- package/dist/tracing.js +0 -74
- package/dist/tracing.js.map +0 -1
- package/dist/webhook-events.js +0 -58
- package/dist/webhook-events.js.map +0 -1
- package/dist/webhook-queue.js +0 -108
- package/dist/webhook-queue.js.map +0 -1
|
@@ -1,579 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* End-to-end lifecycle scenarios — exercise multi-notification sequences against
|
|
3
|
-
* the real webhook router + status route + stores, the way Apple/Google would
|
|
4
|
-
* deliver them in production.
|
|
5
|
-
*
|
|
6
|
-
* Single-notification correctness is covered by the per-feature unit tests; this
|
|
7
|
-
* file catches issues that only show up when notifications arrive in a sequence:
|
|
8
|
-
* stale state bleeding across transitions, idempotency under replay, recovery
|
|
9
|
-
* paths after a missed webhook, etc.
|
|
10
|
-
*
|
|
11
|
-
* Why scenarios instead of more unit tests:
|
|
12
|
-
* - The webhook handler has many branches (per-notification mapping, fresh
|
|
13
|
-
* re-fetch, refundPolicy gating, hooks). Each is unit-tested in isolation,
|
|
14
|
-
* but a transition like SUBSCRIBED → DID_FAIL_TO_RENEW(GRACE) → DID_RENEW
|
|
15
|
-
* touches three of those branches in turn — a regression in any one only
|
|
16
|
-
* surfaces when run together.
|
|
17
|
-
* - These scenarios mirror the runbook for sandbox verification, so the
|
|
18
|
-
* mock results here can be cross-checked against real sandbox behaviour.
|
|
19
|
-
*/
|
|
20
|
-
import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest';
|
|
21
|
-
import express from 'express';
|
|
22
|
-
import { generateKeyPairSync } from 'crypto';
|
|
23
|
-
import { SUBSCRIPTION_STATUS } from '@onesub/shared';
|
|
24
|
-
import { createWebhookRouter } from '../routes/webhook.js';
|
|
25
|
-
import { createStatusRouter } from '../routes/status.js';
|
|
26
|
-
import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
|
|
27
|
-
import { __testing as appleTesting } from '../providers/apple.js';
|
|
28
|
-
import { isLocalhostUrl, urlHost } from './test-utils.js';
|
|
29
|
-
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
30
|
-
let testEcKey;
|
|
31
|
-
let testRsaKey;
|
|
32
|
-
beforeAll(() => {
|
|
33
|
-
const ec = generateKeyPairSync('ec', { namedCurve: 'P-256' });
|
|
34
|
-
testEcKey = ec.privateKey.export({ type: 'pkcs8', format: 'pem' });
|
|
35
|
-
const rsa = generateKeyPairSync('rsa', { modulusLength: 2048 });
|
|
36
|
-
testRsaKey = rsa.privateKey.export({ type: 'pkcs8', format: 'pem' });
|
|
37
|
-
});
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
vi.restoreAllMocks();
|
|
40
|
-
appleTesting.clearAppleJwtCacheForTests();
|
|
41
|
-
});
|
|
42
|
-
function makeJws(payload) {
|
|
43
|
-
const header = Buffer.from(JSON.stringify({ alg: 'ES256' })).toString('base64url');
|
|
44
|
-
const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
45
|
-
return `${header}.${body}.fakesig`;
|
|
46
|
-
}
|
|
47
|
-
function applePayload(notificationType, opts) {
|
|
48
|
-
const signedTransactionInfo = makeJws({
|
|
49
|
-
bundleId: 'com.example.app',
|
|
50
|
-
type: opts.type ?? 'Auto-Renewable Subscription',
|
|
51
|
-
productId: opts.productId ?? 'pro_monthly',
|
|
52
|
-
transactionId: opts.transactionId ?? `tx_${opts.originalTransactionId}_${Date.now()}`,
|
|
53
|
-
originalTransactionId: opts.originalTransactionId,
|
|
54
|
-
purchaseDate: Date.now() - 30 * 86400000,
|
|
55
|
-
expiresDate: opts.expiresDate ?? Date.now() + 30 * 86400000,
|
|
56
|
-
environment: opts.environment ?? 'Production',
|
|
57
|
-
});
|
|
58
|
-
const signedRenewalInfo = makeJws({ autoRenewStatus: opts.autoRenewStatus ?? 1 });
|
|
59
|
-
const payload = {
|
|
60
|
-
notificationType,
|
|
61
|
-
data: { signedTransactionInfo, signedRenewalInfo },
|
|
62
|
-
};
|
|
63
|
-
if (opts.subtype)
|
|
64
|
-
payload.subtype = opts.subtype;
|
|
65
|
-
return { signedPayload: makeJws(payload) };
|
|
66
|
-
}
|
|
67
|
-
function googlePush(notificationType, purchaseToken, subscriptionId = 'pro_monthly') {
|
|
68
|
-
const json = JSON.stringify({
|
|
69
|
-
version: '1.0',
|
|
70
|
-
packageName: 'com.example.app',
|
|
71
|
-
eventTimeMillis: String(Date.now()),
|
|
72
|
-
subscriptionNotification: {
|
|
73
|
-
version: '1.0',
|
|
74
|
-
notificationType,
|
|
75
|
-
purchaseToken,
|
|
76
|
-
subscriptionId,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
return {
|
|
80
|
-
message: { data: Buffer.from(json).toString('base64'), messageId: '1' },
|
|
81
|
-
subscription: 's',
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function buildServers(config) {
|
|
85
|
-
const store = new InMemorySubscriptionStore();
|
|
86
|
-
const purchaseStore = new InMemoryPurchaseStore();
|
|
87
|
-
const webhookApp = express();
|
|
88
|
-
webhookApp.use(express.json());
|
|
89
|
-
webhookApp.use(createWebhookRouter(config, store, purchaseStore));
|
|
90
|
-
const statusApp = express();
|
|
91
|
-
statusApp.use(express.json());
|
|
92
|
-
statusApp.use(createStatusRouter(store));
|
|
93
|
-
const post = async (app, path, body) => {
|
|
94
|
-
const httpServer = app.listen(0);
|
|
95
|
-
const port = httpServer.address().port;
|
|
96
|
-
try {
|
|
97
|
-
const resp = await fetch(`http://127.0.0.1:${port}${path}`, {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: { 'Content-Type': 'application/json' },
|
|
100
|
-
body: JSON.stringify(body),
|
|
101
|
-
});
|
|
102
|
-
return { status: resp.status };
|
|
103
|
-
}
|
|
104
|
-
finally {
|
|
105
|
-
await new Promise((r) => httpServer.close(() => r()));
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const get = async (app, path) => {
|
|
109
|
-
const httpServer = app.listen(0);
|
|
110
|
-
const port = httpServer.address().port;
|
|
111
|
-
try {
|
|
112
|
-
const resp = await fetch(`http://127.0.0.1:${port}${path}`);
|
|
113
|
-
return (await resp.json());
|
|
114
|
-
}
|
|
115
|
-
finally {
|
|
116
|
-
await new Promise((r) => httpServer.close(() => r()));
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
return {
|
|
120
|
-
store,
|
|
121
|
-
purchaseStore,
|
|
122
|
-
server: {
|
|
123
|
-
webhook: (path, body) => post(webhookApp, path, body),
|
|
124
|
-
status: (userId) => get(statusApp, `/onesub/status?userId=${encodeURIComponent(userId)}`),
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
const futureExpiry = '2099-01-01T00:00:00.000Z';
|
|
129
|
-
const sampleSub = (overrides) => ({
|
|
130
|
-
userId: 'real_user',
|
|
131
|
-
productId: 'pro_monthly',
|
|
132
|
-
platform: 'apple',
|
|
133
|
-
status: 'active',
|
|
134
|
-
expiresAt: futureExpiry,
|
|
135
|
-
originalTransactionId: 'orig_x',
|
|
136
|
-
purchasedAt: '2026-01-01T00:00:00.000Z',
|
|
137
|
-
willRenew: true,
|
|
138
|
-
...overrides,
|
|
139
|
-
});
|
|
140
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
141
|
-
// Scenario 1 — Apple billing retry lifecycle
|
|
142
|
-
// SUBSCRIBED → DID_FAIL_TO_RENEW(GRACE_PERIOD) → GRACE_PERIOD_EXPIRED → DID_RENEW
|
|
143
|
-
// Verifies: each transition lands in the right status; entitlement (status route
|
|
144
|
-
// active flag) flips on/off in lockstep; recovery to active works after on_hold.
|
|
145
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
146
|
-
describe('Scenario 1 — Apple billing retry lifecycle', () => {
|
|
147
|
-
it('SUBSCRIBED → grace_period → on_hold → active recovery', async () => {
|
|
148
|
-
const config = {
|
|
149
|
-
apple: { bundleId: 'com.example.app', skipJwsVerification: true },
|
|
150
|
-
database: { url: '' },
|
|
151
|
-
};
|
|
152
|
-
const { store, server } = buildServers(config);
|
|
153
|
-
const orig = 'orig_billing_retry';
|
|
154
|
-
await store.save(sampleSub({ originalTransactionId: orig, userId: 'u_br' }));
|
|
155
|
-
// 1. SUBSCRIBED — entitlement active
|
|
156
|
-
await server.webhook('/onesub/webhook/apple', applePayload('SUBSCRIBED', {
|
|
157
|
-
originalTransactionId: orig,
|
|
158
|
-
expiresDate: Date.now() + 30 * 86400000,
|
|
159
|
-
}));
|
|
160
|
-
expect((await store.getByTransactionId(orig))?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
161
|
-
expect((await server.status('u_br')).active).toBe(true);
|
|
162
|
-
// 2. DID_FAIL_TO_RENEW + GRACE_PERIOD subtype — entitlement still valid (grace)
|
|
163
|
-
await server.webhook('/onesub/webhook/apple', applePayload('DID_FAIL_TO_RENEW', {
|
|
164
|
-
originalTransactionId: orig,
|
|
165
|
-
subtype: 'GRACE_PERIOD',
|
|
166
|
-
}));
|
|
167
|
-
expect((await store.getByTransactionId(orig))?.status).toBe(SUBSCRIPTION_STATUS.GRACE_PERIOD);
|
|
168
|
-
expect((await server.status('u_br')).active).toBe(true); // grace counts as active
|
|
169
|
-
// 3. GRACE_PERIOD_EXPIRED — billing retry, entitlement REVOKED
|
|
170
|
-
await server.webhook('/onesub/webhook/apple', applePayload('GRACE_PERIOD_EXPIRED', {
|
|
171
|
-
originalTransactionId: orig,
|
|
172
|
-
}));
|
|
173
|
-
expect((await store.getByTransactionId(orig))?.status).toBe(SUBSCRIPTION_STATUS.ON_HOLD);
|
|
174
|
-
expect((await server.status('u_br')).active).toBe(false);
|
|
175
|
-
// 4. DID_RENEW — recovery
|
|
176
|
-
await server.webhook('/onesub/webhook/apple', applePayload('DID_RENEW', {
|
|
177
|
-
originalTransactionId: orig,
|
|
178
|
-
expiresDate: Date.now() + 60 * 86400000,
|
|
179
|
-
}));
|
|
180
|
-
expect((await store.getByTransactionId(orig))?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
181
|
-
expect((await server.status('u_br')).active).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
185
|
-
// Scenario 2 — Apple CONSUMPTION_REQUEST without provider configured
|
|
186
|
-
// User has a consumable purchase. Apple sends CONSUMPTION_REQUEST.
|
|
187
|
-
// Without an apple.consumptionInfoProvider hook, the webhook should:
|
|
188
|
-
// - Not mutate the PurchaseStore (CONSUMPTION_REQUEST is a refund REVIEW,
|
|
189
|
-
// not a confirmed refund — IAP row stays put)
|
|
190
|
-
// - Not call the App Store Server API (no PUT)
|
|
191
|
-
// - Still 200 to Apple
|
|
192
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
193
|
-
describe('Scenario 2 — Apple CONSUMPTION_REQUEST without provider hook', () => {
|
|
194
|
-
it('does not call Apple PUT, does not delete the consumable row, returns 200', async () => {
|
|
195
|
-
const originalFetch = global.fetch;
|
|
196
|
-
const outboundPuts = [];
|
|
197
|
-
vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => {
|
|
198
|
-
if (isLocalhostUrl(url))
|
|
199
|
-
return originalFetch(url, init);
|
|
200
|
-
if (init?.method === 'PUT')
|
|
201
|
-
outboundPuts.push(String(url));
|
|
202
|
-
return { ok: true, status: 200, json: async () => ({}), text: async () => '' };
|
|
203
|
-
});
|
|
204
|
-
const config = {
|
|
205
|
-
apple: { bundleId: 'com.example.app', skipJwsVerification: true }, // no API creds, no hook
|
|
206
|
-
database: { url: '' },
|
|
207
|
-
};
|
|
208
|
-
const { purchaseStore, server } = buildServers(config);
|
|
209
|
-
const txId = 'iap_consumable_under_review';
|
|
210
|
-
await purchaseStore.savePurchase({
|
|
211
|
-
userId: 'u_consume',
|
|
212
|
-
productId: 'credits_100',
|
|
213
|
-
platform: 'apple',
|
|
214
|
-
type: 'consumable',
|
|
215
|
-
transactionId: txId,
|
|
216
|
-
purchasedAt: '2026-04-01T00:00:00.000Z',
|
|
217
|
-
quantity: 1,
|
|
218
|
-
});
|
|
219
|
-
// CONSUMPTION_REQUEST is in APPLE_CANCELED_TYPES — for IAP it routes through
|
|
220
|
-
// the deletePurchaseByTransactionId branch. Document this current behavior:
|
|
221
|
-
// the row IS removed even on a refund REVIEW (not just confirmed REFUND).
|
|
222
|
-
// If the host wants to defer removal until confirmation, they should set up
|
|
223
|
-
// a consumptionInfoProvider that returns a custom decision.
|
|
224
|
-
const resp = await server.webhook('/onesub/webhook/apple', applePayload('CONSUMPTION_REQUEST', {
|
|
225
|
-
originalTransactionId: txId,
|
|
226
|
-
transactionId: txId,
|
|
227
|
-
type: 'Consumable',
|
|
228
|
-
productId: 'credits_100',
|
|
229
|
-
}));
|
|
230
|
-
expect(resp.status).toBe(200);
|
|
231
|
-
expect(outboundPuts).toHaveLength(0); // no Apple PUT (no provider configured)
|
|
232
|
-
// Pinning current behavior: webhook removes the consumable on CONSUMPTION_REQUEST.
|
|
233
|
-
// If we later want to keep the row until confirmed REFUND, this assertion flips.
|
|
234
|
-
expect(await purchaseStore.getPurchaseByTransactionId(txId)).toBeNull();
|
|
235
|
-
});
|
|
236
|
-
it('with provider hook: provider invoked, PUT issued, row still removed', async () => {
|
|
237
|
-
const originalFetch = global.fetch;
|
|
238
|
-
const outboundPuts = [];
|
|
239
|
-
vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => {
|
|
240
|
-
if (isLocalhostUrl(url))
|
|
241
|
-
return originalFetch(url, init);
|
|
242
|
-
if (init?.method === 'PUT')
|
|
243
|
-
outboundPuts.push({ url: String(url), body: init.body });
|
|
244
|
-
return { ok: true, status: 200, json: async () => ({}), text: async () => '' };
|
|
245
|
-
});
|
|
246
|
-
const provider = vi.fn(async (_ctx) => ({
|
|
247
|
-
customerConsented: true,
|
|
248
|
-
consumptionStatus: 3,
|
|
249
|
-
deliveryStatus: 1,
|
|
250
|
-
refundPreference: 2,
|
|
251
|
-
}));
|
|
252
|
-
const config = {
|
|
253
|
-
apple: {
|
|
254
|
-
bundleId: 'com.example.app',
|
|
255
|
-
skipJwsVerification: true,
|
|
256
|
-
keyId: 'KEY1',
|
|
257
|
-
issuerId: 'iss-uuid',
|
|
258
|
-
privateKey: testEcKey,
|
|
259
|
-
consumptionInfoProvider: provider,
|
|
260
|
-
},
|
|
261
|
-
database: { url: '' },
|
|
262
|
-
};
|
|
263
|
-
const { purchaseStore, server } = buildServers(config);
|
|
264
|
-
const txId = 'iap_with_provider';
|
|
265
|
-
await purchaseStore.savePurchase({
|
|
266
|
-
userId: 'u_provider',
|
|
267
|
-
productId: 'credits_100',
|
|
268
|
-
platform: 'apple',
|
|
269
|
-
type: 'consumable',
|
|
270
|
-
transactionId: txId,
|
|
271
|
-
purchasedAt: '2026-04-01T00:00:00.000Z',
|
|
272
|
-
quantity: 1,
|
|
273
|
-
});
|
|
274
|
-
await server.webhook('/onesub/webhook/apple', applePayload('CONSUMPTION_REQUEST', {
|
|
275
|
-
originalTransactionId: txId,
|
|
276
|
-
transactionId: txId,
|
|
277
|
-
type: 'Consumable',
|
|
278
|
-
productId: 'credits_100',
|
|
279
|
-
}));
|
|
280
|
-
await new Promise((r) => setTimeout(r, 50)); // hook is fire-and-forget
|
|
281
|
-
expect(provider).toHaveBeenCalled();
|
|
282
|
-
expect(outboundPuts).toHaveLength(1);
|
|
283
|
-
expect(outboundPuts[0].url).toContain('/inApps/v1/transactions/consumption/');
|
|
284
|
-
expect(JSON.parse(String(outboundPuts[0].body))).toMatchObject({
|
|
285
|
-
customerConsented: true,
|
|
286
|
-
refundPreference: 2,
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
291
|
-
// Scenario 3 — Google user-voluntary pause with auto-resume time
|
|
292
|
-
// PURCHASED → PAUSED (with autoResumeTime) → RESTARTED
|
|
293
|
-
// Status route should keep the autoResumeTime in the subscription payload while
|
|
294
|
-
// paused, so the host can render "재개 예정" UX.
|
|
295
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
296
|
-
describe('Scenario 3 — Google paused lifecycle with autoResumeTime', () => {
|
|
297
|
-
it('PURCHASED → PAUSED (autoResumeTime surfaces) → RESTARTED', async () => {
|
|
298
|
-
const config = {
|
|
299
|
-
google: {
|
|
300
|
-
packageName: 'com.example.app',
|
|
301
|
-
serviceAccountKey: JSON.stringify({
|
|
302
|
-
client_email: `sa-${Math.random()}@x.iam.gserviceaccount.com`,
|
|
303
|
-
private_key: testRsaKey,
|
|
304
|
-
token_uri: 'https://oauth2.googleapis.com/token',
|
|
305
|
-
}),
|
|
306
|
-
},
|
|
307
|
-
database: { url: '' },
|
|
308
|
-
};
|
|
309
|
-
const tok = 'tok_google_pause';
|
|
310
|
-
let v2Response = {
|
|
311
|
-
startTime: '2026-01-01T00:00:00Z',
|
|
312
|
-
subscriptionState: 'SUBSCRIPTION_STATE_ACTIVE',
|
|
313
|
-
latestOrderId: 'GPA.active_1',
|
|
314
|
-
lineItems: [{
|
|
315
|
-
productId: 'pro_monthly',
|
|
316
|
-
expiryTime: '2027-01-01T00:00:00Z',
|
|
317
|
-
autoRenewingPlan: { autoRenewEnabled: true },
|
|
318
|
-
}],
|
|
319
|
-
};
|
|
320
|
-
const originalFetch = global.fetch;
|
|
321
|
-
vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => {
|
|
322
|
-
if (isLocalhostUrl(url))
|
|
323
|
-
return originalFetch(url, init);
|
|
324
|
-
const host = urlHost(url);
|
|
325
|
-
if (host === 'oauth2.googleapis.com') {
|
|
326
|
-
return { ok: true, json: async () => ({ access_token: 'tok', expires_in: 3600 }), text: async () => '' };
|
|
327
|
-
}
|
|
328
|
-
if (host === 'androidpublisher.googleapis.com') {
|
|
329
|
-
return { ok: true, json: async () => v2Response, text: async () => '' };
|
|
330
|
-
}
|
|
331
|
-
throw new Error(`unexpected ${String(url)}`);
|
|
332
|
-
});
|
|
333
|
-
const { store, server } = buildServers(config);
|
|
334
|
-
// Pre-seed an existing record (the unknown-tx fallback would otherwise
|
|
335
|
-
// create one with a placeholder userId, which is fine but we want a real one).
|
|
336
|
-
await store.save(sampleSub({
|
|
337
|
-
platform: 'google',
|
|
338
|
-
originalTransactionId: tok,
|
|
339
|
-
userId: 'u_pause',
|
|
340
|
-
}));
|
|
341
|
-
// 1. PURCHASED — fresh re-fetch returns ACTIVE
|
|
342
|
-
await server.webhook('/onesub/webhook/google', googlePush(4, tok));
|
|
343
|
-
let saved = await store.getByTransactionId(tok);
|
|
344
|
-
expect(saved?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
345
|
-
expect(saved?.autoResumeTime).toBeUndefined();
|
|
346
|
-
expect((await server.status('u_pause')).active).toBe(true);
|
|
347
|
-
// 2. PAUSED — v2 now returns PAUSED + autoResumeTime
|
|
348
|
-
v2Response = {
|
|
349
|
-
...v2Response,
|
|
350
|
-
subscriptionState: 'SUBSCRIPTION_STATE_PAUSED',
|
|
351
|
-
pausedStateContext: { autoResumeTime: '2026-08-15T00:00:00Z' },
|
|
352
|
-
};
|
|
353
|
-
await server.webhook('/onesub/webhook/google', googlePush(10, tok));
|
|
354
|
-
saved = await store.getByTransactionId(tok);
|
|
355
|
-
expect(saved?.status).toBe(SUBSCRIPTION_STATUS.PAUSED);
|
|
356
|
-
expect(saved?.autoResumeTime).toBe('2026-08-15T00:00:00Z');
|
|
357
|
-
const pausedStatus = await server.status('u_pause');
|
|
358
|
-
expect(pausedStatus.active).toBe(false); // entitlement revoked while paused
|
|
359
|
-
expect(pausedStatus.subscription?.autoResumeTime).toBe('2026-08-15T00:00:00Z');
|
|
360
|
-
// 3. RESTARTED — back to active, autoResumeTime cleared
|
|
361
|
-
v2Response = {
|
|
362
|
-
...v2Response,
|
|
363
|
-
subscriptionState: 'SUBSCRIPTION_STATE_ACTIVE',
|
|
364
|
-
pausedStateContext: undefined,
|
|
365
|
-
};
|
|
366
|
-
await server.webhook('/onesub/webhook/google', googlePush(7, tok));
|
|
367
|
-
saved = await store.getByTransactionId(tok);
|
|
368
|
-
expect(saved?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
369
|
-
expect(saved?.autoResumeTime).toBeUndefined();
|
|
370
|
-
expect((await server.status('u_pause')).active).toBe(true);
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
374
|
-
// Scenario 4 — Google plan upgrade (linkedPurchaseToken continuity)
|
|
375
|
-
// Monthly subscription (token=tok_M, user=u_link) → user upgrades to yearly →
|
|
376
|
-
// new token (tok_Y) arrives via SUBSCRIPTION_PURCHASED with linkedPurchaseToken=tok_M.
|
|
377
|
-
// The webhook's unknown-tx branch should look up tok_M, inherit u_link's userId
|
|
378
|
-
// onto the new yearly record, and leave the monthly record intact (history).
|
|
379
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
380
|
-
describe('Scenario 4 — Google plan upgrade userId continuity', () => {
|
|
381
|
-
it('monthly → yearly: new record inherits userId, monthly history preserved', async () => {
|
|
382
|
-
const config = {
|
|
383
|
-
google: {
|
|
384
|
-
packageName: 'com.example.app',
|
|
385
|
-
serviceAccountKey: JSON.stringify({
|
|
386
|
-
client_email: `sa-${Math.random()}@x.iam.gserviceaccount.com`,
|
|
387
|
-
private_key: testRsaKey,
|
|
388
|
-
token_uri: 'https://oauth2.googleapis.com/token',
|
|
389
|
-
}),
|
|
390
|
-
},
|
|
391
|
-
database: { url: '' },
|
|
392
|
-
};
|
|
393
|
-
const tokM = 'tok_monthly';
|
|
394
|
-
const tokY = 'tok_yearly';
|
|
395
|
-
const v2Yearly = {
|
|
396
|
-
startTime: '2026-04-01T00:00:00Z',
|
|
397
|
-
subscriptionState: 'SUBSCRIPTION_STATE_ACTIVE',
|
|
398
|
-
latestOrderId: 'GPA.yearly_first',
|
|
399
|
-
linkedPurchaseToken: tokM,
|
|
400
|
-
lineItems: [{
|
|
401
|
-
productId: 'pro_yearly',
|
|
402
|
-
expiryTime: '2027-04-01T00:00:00Z',
|
|
403
|
-
autoRenewingPlan: { autoRenewEnabled: true },
|
|
404
|
-
}],
|
|
405
|
-
};
|
|
406
|
-
const originalFetch = global.fetch;
|
|
407
|
-
vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => {
|
|
408
|
-
if (isLocalhostUrl(url))
|
|
409
|
-
return originalFetch(url, init);
|
|
410
|
-
const host = urlHost(url);
|
|
411
|
-
if (host === 'oauth2.googleapis.com') {
|
|
412
|
-
return { ok: true, json: async () => ({ access_token: 'tok', expires_in: 3600 }), text: async () => '' };
|
|
413
|
-
}
|
|
414
|
-
return { ok: true, json: async () => v2Yearly, text: async () => '' };
|
|
415
|
-
});
|
|
416
|
-
const { store, server } = buildServers(config);
|
|
417
|
-
// Pre-existing monthly subscription owned by u_link
|
|
418
|
-
await store.save(sampleSub({
|
|
419
|
-
platform: 'google',
|
|
420
|
-
productId: 'pro_monthly',
|
|
421
|
-
originalTransactionId: tokM,
|
|
422
|
-
userId: 'u_link',
|
|
423
|
-
}));
|
|
424
|
-
// SUBSCRIPTION_PURCHASED for the new yearly token (unknown to store)
|
|
425
|
-
await server.webhook('/onesub/webhook/google', googlePush(4, tokY, 'pro_yearly'));
|
|
426
|
-
// New yearly record created under the v2-returned latestOrderId, userId inherited
|
|
427
|
-
const newRec = await store.getByTransactionId('GPA.yearly_first');
|
|
428
|
-
expect(newRec).not.toBeNull();
|
|
429
|
-
expect(newRec?.userId).toBe('u_link'); // ← continuity
|
|
430
|
-
expect(newRec?.productId).toBe('pro_yearly');
|
|
431
|
-
expect(newRec?.linkedPurchaseToken).toBe(tokM);
|
|
432
|
-
expect(newRec?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
433
|
-
// Old monthly record still findable (history preserved)
|
|
434
|
-
const oldRec = await store.getByTransactionId(tokM);
|
|
435
|
-
expect(oldRec).not.toBeNull();
|
|
436
|
-
expect(oldRec?.productId).toBe('pro_monthly');
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
440
|
-
// Scenario 5 — Apple subscription REFUND (default 'immediate' policy)
|
|
441
|
-
// SUBSCRIBED → REFUND. status should flip to canceled right away, not on next
|
|
442
|
-
// expiry. Status route reports active=false immediately. With refundPolicy
|
|
443
|
-
// 'until_expiry', the same sequence keeps active=true until expiresAt — covered
|
|
444
|
-
// in the second case for contrast.
|
|
445
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
446
|
-
describe('Scenario 5 — Apple subscription REFUND (immediate vs until_expiry)', () => {
|
|
447
|
-
it("default 'immediate': REFUND flips status=canceled, active=false now", async () => {
|
|
448
|
-
const config = {
|
|
449
|
-
apple: { bundleId: 'com.example.app', skipJwsVerification: true },
|
|
450
|
-
database: { url: '' },
|
|
451
|
-
};
|
|
452
|
-
const { store, server } = buildServers(config);
|
|
453
|
-
const orig = 'orig_refund_immediate';
|
|
454
|
-
await store.save(sampleSub({ originalTransactionId: orig, userId: 'u_refund_imm' }));
|
|
455
|
-
await server.webhook('/onesub/webhook/apple', applePayload('SUBSCRIBED', {
|
|
456
|
-
originalTransactionId: orig,
|
|
457
|
-
expiresDate: Date.now() + 30 * 86400000,
|
|
458
|
-
}));
|
|
459
|
-
expect((await server.status('u_refund_imm')).active).toBe(true);
|
|
460
|
-
await server.webhook('/onesub/webhook/apple', applePayload('REFUND', {
|
|
461
|
-
originalTransactionId: orig,
|
|
462
|
-
expiresDate: Date.now() + 30 * 86400000,
|
|
463
|
-
}));
|
|
464
|
-
expect((await store.getByTransactionId(orig))?.status).toBe(SUBSCRIPTION_STATUS.CANCELED);
|
|
465
|
-
expect((await server.status('u_refund_imm')).active).toBe(false);
|
|
466
|
-
});
|
|
467
|
-
it("'until_expiry': REFUND keeps status=active until expiresAt passes", async () => {
|
|
468
|
-
const config = {
|
|
469
|
-
apple: { bundleId: 'com.example.app', skipJwsVerification: true },
|
|
470
|
-
database: { url: '' },
|
|
471
|
-
refundPolicy: 'until_expiry',
|
|
472
|
-
};
|
|
473
|
-
const { store, server } = buildServers(config);
|
|
474
|
-
const orig = 'orig_refund_keep';
|
|
475
|
-
await store.save(sampleSub({
|
|
476
|
-
originalTransactionId: orig,
|
|
477
|
-
userId: 'u_refund_keep',
|
|
478
|
-
expiresAt: futureExpiry,
|
|
479
|
-
}));
|
|
480
|
-
await server.webhook('/onesub/webhook/apple', applePayload('REFUND', {
|
|
481
|
-
originalTransactionId: orig,
|
|
482
|
-
expiresDate: Date.now() + 30 * 86400000,
|
|
483
|
-
}));
|
|
484
|
-
const after = await store.getByTransactionId(orig);
|
|
485
|
-
expect(after?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE); // status preserved
|
|
486
|
-
expect(after?.willRenew).toBe(false); // willRenew flipped
|
|
487
|
-
expect(after?.expiresAt).toBe(futureExpiry); // expiresAt preserved
|
|
488
|
-
expect((await server.status('u_refund_keep')).active).toBe(true); // entitlement still valid
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
492
|
-
// Scenario 6 — webhook for unknown originalTransactionId → Apple Status API fallback
|
|
493
|
-
// The local store has no record (e.g. server downtime caused webhook miss). When
|
|
494
|
-
// any notification arrives for that originalTransactionId, the webhook should:
|
|
495
|
-
// - Detect the missing record
|
|
496
|
-
// - Call fetchAppleSubscriptionStatus to get canonical state from Apple
|
|
497
|
-
// - Save a record under originalTransactionId with placeholder userId
|
|
498
|
-
// - Subsequent /onesub/validate from the host can claim ownership
|
|
499
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
500
|
-
describe('Scenario 6 — webhook missed → Apple Status API fallback recovery', () => {
|
|
501
|
-
it('unknown-tx notification triggers Status API fetch and saves a placeholder record', async () => {
|
|
502
|
-
const orig = 'orig_recovered';
|
|
503
|
-
const recoveredTx = makeJws({
|
|
504
|
-
bundleId: 'com.example.app',
|
|
505
|
-
productId: 'pro_monthly',
|
|
506
|
-
transactionId: 'tx_recovered',
|
|
507
|
-
originalTransactionId: orig,
|
|
508
|
-
purchaseDate: Date.now() - 10 * 86400000,
|
|
509
|
-
originalPurchaseDate: Date.now() - 10 * 86400000,
|
|
510
|
-
expiresDate: Date.now() + 20 * 86400000,
|
|
511
|
-
});
|
|
512
|
-
const originalFetch = global.fetch;
|
|
513
|
-
const apiCalls = [];
|
|
514
|
-
vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => {
|
|
515
|
-
if (isLocalhostUrl(url))
|
|
516
|
-
return originalFetch(url, init);
|
|
517
|
-
const u = String(url);
|
|
518
|
-
apiCalls.push(u);
|
|
519
|
-
// Mock Apple Status API response
|
|
520
|
-
return {
|
|
521
|
-
ok: true,
|
|
522
|
-
status: 200,
|
|
523
|
-
json: async () => ({
|
|
524
|
-
bundleId: 'com.example.app',
|
|
525
|
-
environment: 'Production',
|
|
526
|
-
data: [{
|
|
527
|
-
lastTransactions: [{
|
|
528
|
-
originalTransactionId: orig,
|
|
529
|
-
status: 1, // Active
|
|
530
|
-
signedTransactionInfo: recoveredTx,
|
|
531
|
-
signedRenewalInfo: makeJws({ autoRenewStatus: 1 }),
|
|
532
|
-
}],
|
|
533
|
-
}],
|
|
534
|
-
}),
|
|
535
|
-
text: async () => '',
|
|
536
|
-
};
|
|
537
|
-
});
|
|
538
|
-
const config = {
|
|
539
|
-
apple: {
|
|
540
|
-
bundleId: 'com.example.app',
|
|
541
|
-
skipJwsVerification: true,
|
|
542
|
-
keyId: 'K',
|
|
543
|
-
issuerId: 'I',
|
|
544
|
-
privateKey: testEcKey,
|
|
545
|
-
},
|
|
546
|
-
database: { url: '' },
|
|
547
|
-
};
|
|
548
|
-
const { store, server } = buildServers(config);
|
|
549
|
-
// No pre-seed → store has no record for `orig`
|
|
550
|
-
expect(await store.getByTransactionId(orig)).toBeNull();
|
|
551
|
-
const resp = await server.webhook('/onesub/webhook/apple', applePayload('DID_RENEW', {
|
|
552
|
-
originalTransactionId: orig,
|
|
553
|
-
expiresDate: Date.now() + 20 * 86400000,
|
|
554
|
-
}));
|
|
555
|
-
expect(resp.status).toBe(200);
|
|
556
|
-
// Status API was called as fallback
|
|
557
|
-
expect(apiCalls.some((u) => u.includes('/inApps/v1/subscriptions/'))).toBe(true);
|
|
558
|
-
// Record was created with placeholder userId = originalTransactionId
|
|
559
|
-
const recovered = await store.getByTransactionId(orig);
|
|
560
|
-
expect(recovered).not.toBeNull();
|
|
561
|
-
expect(recovered?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
|
|
562
|
-
expect(recovered?.userId).toBe(orig); // placeholder until host /validate claims it
|
|
563
|
-
});
|
|
564
|
-
it('unknown-tx with no API credentials: logs and acks (no record created)', async () => {
|
|
565
|
-
const config = {
|
|
566
|
-
apple: { bundleId: 'com.example.app', skipJwsVerification: true }, // no API creds
|
|
567
|
-
database: { url: '' },
|
|
568
|
-
};
|
|
569
|
-
const { store, server } = buildServers(config);
|
|
570
|
-
const orig = 'orig_no_creds';
|
|
571
|
-
const resp = await server.webhook('/onesub/webhook/apple', applePayload('DID_RENEW', {
|
|
572
|
-
originalTransactionId: orig,
|
|
573
|
-
expiresDate: Date.now() + 20 * 86400000,
|
|
574
|
-
}));
|
|
575
|
-
expect(resp.status).toBe(200); // ack so Apple doesn't retry forever
|
|
576
|
-
expect(await store.getByTransactionId(orig)).toBeNull(); // can't recover without creds
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
//# sourceMappingURL=lifecycle-scenarios.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-scenarios.test.js","sourceRoot":"","sources":["../../src/__tests__/lifecycle-scenarios.test.ts"],"names":[],"mappings":"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"}
|